栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++面向对象程序设计入门(第一节)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++面向对象程序设计入门(第一节)

注:本系列博客适合有一定C语言基础的学习者,旨在介绍C++面向对象的编程思想以及其语法实现。

目录

1 宏

1.1 常量宏

1.2 函数宏

1.3 控制宏

 1.4 其他

2 class与struct

2.1 区别

2.2 对齐方式

3C++内存分区

本节将主要介绍后续学习过程中所需的基础知识,帮助读者加深对C语言的了解的同时逐步过渡到C++的学习之中。

1 宏

        想要理解C语言中宏的作用方式,需要提前大致了解C语言源程序的编译过程:我们通常所说的编译其实细分为四个阶段:预处理、编译、汇编、链接。宏本质上是一种预处理指令,简单来说,其会在编译器发挥作用前,将所有的预处理指令进行简单的文本替换。例如我们熟知的"#include"指令便是指示预处理器打开一个名字为stdio.h的文件,并将它的内容"包含"到当前的程序中;"#define"则是指示预处理器用实际值替换宏定义的字符串。后续内容中我们会还会见到的"#pragma"等,都是重要的预处理指令。

        本节中我们会用到的预处理指令如下:

1.1 常量宏

        从分类上讲,常量宏属于不含参数的宏的范畴,其可能在日常编程解题中出现不多,但是随着代码量的增大,代码复杂程度增加,其作用会愈发明显。

#define MAX_OP 100000

        例如我们可以将100000定义为MAX_OP,在程序中所有与max option(最大操作数)有关的均用MAX_OP代替,这样

  • 一方面可以消除“神仙数”,即赋予100000以含义,方便以后阅读程序时,便于理解相关操作的含义。
  • 另一方面可以方便修改,当我们的程序在运算大量数据出现错误时,我们通常需要减小数据强度,这时我们仅需要修改宏定义的值,而无需修改每一处100000的值。

1.2 函数宏
#define square(x) ((x)*(x))

        例如上图所示,函数宏的写法与函数略有不同,例如其可以没有形参类型,没有返回值类型等,我们在写函数宏时,需要明确把握一个关键点——函数宏只做文本替换,不做函数调用。即程序中所有square(x)出现的地方都会被替换为 ((x)*(x)),那么易知函数宏可以程序的运行速度提高(因为没有函数的调用)。但是除去一些比较经典的函数宏(如swap)外,并不建议大家多写函数宏,因为正是宏的简单文本替换这个特性,可能会导致一些很难发现的错误,例如下图程序的执行结果是11,并非我们预想的25,这是因为程序先进行了第一个add中y和第二个add中x的相乘,并非按照我们想象的先执行两个add之后再相乘。

#include 
#define add(x,y) x+y

int main()
{
    int x = 2, y = 3;
    int z = add(x,y) * add(x,y);
    printf("%d", z);
    return 0;
}

        以及下图这种情况,就是由于我们在写C程序时想当然的在句末添加了' ; ',但是却忽视了print宏中含有了花括号,而在花括号末尾加分号则表示了语句的结束,故造成了编译报错else没有对应的if。

         另外在这里分享几个比较好用的宏函数供大家参考~

#define swap(a, b) { a ^= b; b ^= a; a ^= b; } 
//对于a,b为整型时,可以调用该宏进行交换数值。

#define swap(a, b) { 
    __typeof(a) temp = a; 
    a = b, b = temp; 
}
//这种交换宏比较通用,其除了可以交换内置类型外,还可以交换我们自定义的两个结构体变量的值
//但是我们一般不推荐直接交换结构体对象,我们更倾向于交换结构体指针
//这涉及到了深拷贝与浅拷贝的相关内容,我们会在后续详细讲解

1.3 控制宏

        控制宏的写法略有些不同,其没有替换文本,仅仅是定义一个事物,其发挥作用的方式类似于我们日常生活中的button。例如:

#define 'something' 

        如果我们对C/C++的头文件有所了解的话,我们会发现其中有很多的#ifdef、#ifndef、#endif等语句,例如下图,其含义为:如果我实现定义了#ifdef 后面的这个宏,那我就执行其作用域内的四个操作,并在最后声明结束‘#endif’。头文件中之所以有这么多的条件结构,还有一方面原因是为了防止头文件被多次包含,变量重复定义等出错。

         例如我们以后会谈到的C++异常处理中的“断言”语法也涉及到了一个控制宏的使用。当我们定义了NDEBUG以后,程序中所有的断言将不再生效。(具体实现可参考assert.h头文件中的内容)

 1.4 其他

        C/C++中宏的使用是大部分初学者容易忽视的一部分重要内容,合适的使用宏定义可以使我们的程序更加严谨、有序。受限于博客内容,本文未涉及到可选参数(__VA_ARGS__ 和‘ ... ’),字符串化运算符#等等,有兴趣的读者可以另行了解。

2 class与struct

2.1 区别

        在C++中,class与struct非常相似,都属于container类型,其中C++的struct与C语言中的struct并不相同,C++中struct既可以包含成员变量,又可以包含成员函数,而C语言只能包含成员变量。

         而在C++中,class于struct也并不完全相同,两者之间有许多细小的差别,这里仅介绍与我们相关度较大的几个:

  • 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的;
  • class 继承默认是 private 继承,而 struct 继承默认是 public 继承;
  • class 可以使用模板,而 struct 不能。

在本系列博客中,我们推荐全部使用class来实现相关操作。

2.2 对齐方式

        在C++中,为了提高程序运行效率,程序会对类的成员变量进行一系列的对齐。例如在下图所示代码中,如果利用sizeof输出A和B的大小,会发现A对应24,而B对应16,说明AB中均按照最长的字节对齐,AB中最长均为8,而B中int占据4个,还剩4个足够char储存,剩下double另开8个,共16个。而A中int占四个以后不够double储存,所以会另开8个,同理char也会另开8个,说明int和char虽然不足8个但为了程序运行效率,都占据了8个字节的空间。

        对于这种情况,我们可以使用#pragma pack(1)指令来取消对齐,此时类的大小即为成员变量大小之和。(注:此情况不涉及虚函数与虚指针)

#pragma pack(1)
class A{
	int a;
	double b;
	char c;
};
class B{
	int a;
	char c;
	double b;
};

3C++内存分区

        在C++程序中,内存大致可以划分为4个区域:代码区、全局区、栈区和堆区。

  • 代码区:存放程序的二进制代码,此部分由操作系统进行管理。特点是只读和共享。
  • 全局区:全局变量和静态局部变量存放于此处,同时全局区还包括了常量区,字符串常量和其他常量等存放于该区,此区域的数据在程序运行结束后由操作系统自动释放。
  • 栈区:存放局部变量的值、函数的形参等,由编译器自动分配释放。其特点是内存连续,速度较快,并且是不由程序员人为控制的。
  • 堆区:若不细分讨论自由存储区等概念,我们可以大致认为动态分配的内存都会存储在堆区,包括C语言中的malloc(free),还有C++中的new(delete)分配的内存。此区域的特点是由人为控制,数据不连续,相对来说速度较慢,但生存周期一般来说长于栈区。

新人作者,如有纰漏还请各位大佬指出~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/861623.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号