文章目录
菜狗程序员 C++ 万字总结,从入门到入土(上篇)
C++ 语法
namespace
namespace 用来解决 C 语言命名缺陷问题,通过使用 namespace 可以定义一个域。因为 C 语言容易产生命名冲突,也就是变量或函数的重定义,故可用 namespace 将同名变量或函数隔离,从而解决命名冲突问题。
缺省参数
缺省参数就是可以在函数定义或声明时给函数从右向左连续的参数提供一个默认值。缺省参数不能再声明和定义时同时存在。
函数重载
函数重载就是在同一个域中,存在函数名相同,参数类型或者个数不同的函数。C 语言是不支持函数重载的,C++ 通过函数名修饰规则来解决这个问题。
C/C++ 编译过程;
- 预处理:头文件展开、去注释、宏替换、条件编译,生成 .i文件
- 编译:检查语法,生成 .s汇编文件
- 汇编:把汇编代码转换成 .o二进制代码文件
- 链接:将.o文件链接到一起生成可执行文件,也就是在上一步生成文件中找到对应指令的地址。
C++ 不同于 C 语言的是:C++ 在汇编阶段生成的二进制文件中所包含的符号表中的函数命名方式不是直接使用函数名,而是使用函数名进行修饰,也就是将函数参数的类型和个数两者的信息通过某种方式添加到函数名中,从而可以调用函数名相同的函数。
引用
引用从编译角度看,引用和指针是一样的。从语法角度看,引用是个变量取别名。因为 C 语言指针使用过于复杂,C++ 通过引用简化了指针的使用。引用在定义时必须先初始化。在赋值符号两端存在类型偏差时,就会产生临时变量,此时引用的就是临时变量。
引用使用场景有两个:
- 作函数参数,作用类似于 C 语言的传出参数
- 作函数返回值,当函数采用传值返回时,return 的变量会被拷贝一份作为临时变量,函数返回的是临时变量,临时变量具有常性;当函数采用引用返回时,也会产生临时变量,只不过此时这个临时变量是 return 变量的引用(别名),并且这个临时变量不占用内存。
指针和引用的区别:
- 引用必须在定义时初始化,指针不用
- 引用在初始化后就不能引用其它实体,指针可以
- sizeof 引用的大小为引用类型的大小,指针永远都是4/8字节
- 引用没有 NULL 引用,指针有 NULL 指针
- 引用加 1 就是引用的实体类型 加 1,指针加 1 则是指针向后偏移一个类型的大小
内联函数
通过在函数前添加关键字 inline 可以将函数变成内联函数,内联函数在调用时,会直接在调用的地方展开指令,因此对于一些频繁调用的小函数适合定义成内联。内联函数主要为了解决 C 语言宏函数的缺陷:
- 宏函数不支持调试
- 宏函数容易出错
- 宏函数没有类型安全检查
直接在类中定义的函数,默认就是内联函数。
auto
自动推导类型。不能用作函数形参和定义数组。
范围 for
自动遍历可迭代对象,直到结束。
nullptr
空指针,用来替换 NULL。因为 NULL 就是 0,不是指针类型。
C++ 类与对象
类的定义
class 类名
{
public:
// 成员函数声明
void Init();
...
private:
// 成员变量声明
int age;
char* name;
};
// 成员函数定义
void 类名::Init()
{
}
// 类的实例化,同时也完成了成员变量的定义
类名 S;
其中,成员函数也可以在类域中直接定义。public 和 private 为访问限定符,一般想要给外部访问用 public,外部不能访问的用 private。
C++ 也可以用 struct 来声明类,不同的是:struct 成员默认访问方式为 public,class 默认访问方式为 private。
面向对象三大特性
- 继承:保持原有类基础上进行扩展。
- 多态:不同类型的对象同一行为的多种形态。
- 封装:将对象的数据和操作数据的方法进行结合,隐藏对象的属性和实现细节,仅通过开发的接口来和对象进行交互。
类对象只保存成员变量,成员函数存放在公共代码段。
this 指针
C++ 给类对象的非静态成员函数提供了一个隐藏的 this 指针,指针指向调用该方法的类对象,在成员函数中对成员变量的操作都是通过该指针去完成的。
默认(缺省)成员函数
- 构造函数:用于初始化对象,不是开辟空间创建对象。对于 C++ 内置类型的成员变量,默认构造函数不会初始化;对于自定义类型,默认构造函数会调用自定义类型的默认构造函数。默认的构造函数就是不用传递参数的构造函数。
- 析构函数;用于对象销毁时完成类的一些资源清理工作。对于 C++ 内置类型的成员变量,默认析构函数不处理;对于自定义类型,默认析构函数会调用自定义类型的默认析构函数。
- 拷贝构造函数:拷贝构造函数也是一种构造函数(构造函数的重载),函数名与类名相同,参数是对本类型的类型对象的引用,并且一般用 const 修饰。对于 C++ 内置类型的成员变量,默认拷贝函数完成值拷贝(浅拷贝);对于自定义类型,默认拷贝构造函数会调用自定义类型的拷贝构造函数。函数自定义类型参数传递时,
- 赋值运算符重载:也是拷贝行为,不同的是,赋值拷贝是两个对象已经初始化过了,拷贝构造是拿一个已经初始化过了的对象去拷贝一个未初始化的对象。对于内置类型和自定义类型和拷贝构造函数的处理一致。在运算符重载时,有 5 个运算符不能重载:.*、?:、.、sizeof、::。当重载前置++和后置++时,后置++需要在增加一个int参数,构成重载。
const
用 const 修饰的类成员函数称之为 const 成员函数,const 实际修饰的是 this 所指向的对象,因此在该成员函数中不能对任何成员进行修改。
友元
友元打破类的封装,通过友元可以在类外部访问类的私有成员。
初始化列表
在 C++ 中创建对象时,会调用构造函数进行初始化,严格地讲,应该叫赋初值,因为构造函数体内可以多次赋值,而初始化只能执行一次。在初始化列表中,每个成员只出现一次,因此只能初始化一次。当成员变量包含引用类型、const 类型、以及没有默认构造函数的自定义类型时,必须在初始化列表中对这些变量进行初始化。成员变量在类中的声明顺序就是初始化顺序。
单参数的构造函数支持隐式类型转换。
class A
{
public:
A(int a):_a(a)
{
}
private:
int _a;
};
// 实际上会先用 2 构造一个临时对象,然后用这个临时对象去拷贝构造 a,可以用 explicit 关键字来屏蔽上述操作
A a = 2;
static 成员
static 修饰的类成员为静态成员,静态成员为所有类对象共享,不属于某个具体实例,静态成员可用类名::静态成员或者对象.静态成员访问。静态成员变量必须在类外定义,静态成员函数没有隐藏的 this 指针,不能访问非静态成员。
C++ 11 成员初始化
非静态成员变量在声明时,可以添加缺省值,给成员初始化时赋值。在初始化列表中没有内置类型成员初始化,就用缺省值初始化,若没有缺省值,那就是随机值。
内部类
内部类是其外部类的友元。sizeof(外部类)和内部类无关。内部类可以直接访问外部类静态成员。
内存管理
虚拟内存分布
栈(局部变量)、堆(动态申请空间)、静态区(数据段)包含全局数据和静态数据、常量区(代码段)包含代码生成的指令和常量。
C/C++ 内存管理
C 语言:
- malloc 在堆上动态开空间
- calloc 在堆上动态开空间,并且初始化为 0
- realloc 针对已有的空间进行扩容
C++:采用 new 和 delete 动态申请和释放内存。对于内置类型,和 malloc、free 没有区别。针对自定义类型,new 还会调用自定义类型的构造函数,delete 会先调用析构函数再释放空间。
在 32 位平台下,一般能动态申请 2G 的内存空间。在 64 位下,能申请 4G 的内存空间。
new = operator new + 构造函数(.ctor),而 operator new = malloc + 失败抛异常
delete = 析构函数 + operator delete
定位 new:已经分配的空间上调用构造函数初始化一个对象。
malloc/free 和 new/delete 区别:
- malloc/free 是函数,new/delete 是操作符
- malloc 申请的空间不会初始化, new 可以初始化
- malloc 申请空间需要手动计算大小,new 只需传递申请空间的类型
- malloc 返回值是 void*, 在使用时必须强转,new 不需要
- malloc 申请空间失败返回 NULL,在使用时需要判空,new 不需要,但 new 必须捕获异常
- 申请自定义类型时,malloc 和 free 会申请和释放空间,new 和 delete 则是会额外调用构造函数和析构函数
模板