一、基础知识:
1.构造和析构
类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。
构造顺序:父类->子类
析构顺序:子类->父类
2.函数重载和运算符重载
-
函数重载:在同一作用域内可以声明多个重名函数,其函数参数的个数或顺序或类型必须不同,不能仅通过返回类型的不同来重载函数。
-
运算符重载:运算符重载函数是类的成员函数(法一)时,它的第一个操作数是调用该函数的对象(即 this 指针指向的对象),第二个操作数是通过参数传递的对象。
Box operator+(const Box& b) {
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
上述代码是针对+的重载,可以实现两个Box对象的相加。
还有一种方式是通过全局函数的方式重载(定义在类外面)
Box operator+(const Box& a, const Box& b) {
Box box;
box.setLength(a.getLength() + b.getLength());
box.setBreadth(a.getBreadth() + b.getBreadth());
box.setHeight(a.getHeight() + b.getHeight());
return box;
}
调用方式依然是Box3=Box1+Box2。此时编译器会将其解释为Box3 = operator+(Box1, Box2);
3.结构体内存对齐
含义:以空间换时间,使一次访存可以取出一条指令,加快存取速度,常用于RISC指令流水线。
// 可查看struct大小
struct Example {
char a; // 1 字节
int b; // 4 字节
double c; // 8 字节
};
cout << "Size of Example: " << sizeof(Example) << endl;
如何控制内存对齐方式?
1.c++11提供了alignas,可以指定结构体对齐方式。
struct alignas(16) Example {
char a;
int b;
double c;
};
注意事项:alignas的对齐值必须是2的幂次倍,且不能小于结构体最小成员对齐值。
当指定数值小于默认值的时候,alignas无效。
2.#pragma pack(push,n)
alignas无效时,使用#pragma强制将对齐方式设置为 n 字节
二、指针和引用
1.指针
- 指针是一个变量,存储的是另一个变量的内存地址
- 指针可以指向任意类型的对象,包括空指针
- 指针可以在任何时候被重新赋值
- 在定义中使用 * 声明指针,使用&获取地址。
int a = 10;
int* p = &a;
std::cout << "p=" << p << " *p=" << *p << "\n";
输出结果为p=BAF0DDF750 *p=10
p是指针,存的是所指变量的地址,*p获取所指变量的值
2.引用
- 使用 & 声明引用,绑定后不能更换
- 引用直接访问绑定的变量,无需解引用
- 引用是一个变量的别名,在定义时必须初始化
- 引用不可以为空,必须绑定到一个有效对象
int a = 10,b=20;
int& ref = a; // ref绑定到a,ref 是 a 的别名
// int& ref2; // 错误:引用必须初始化
// ref = b; // 这不是重新绑定,而是将 b 的值赋给 a
3.指针和变量的区别
- 指针是一个独立的变量,占用内存。引用占不占内存需要看编译器
- 指针有多级指针,引用只有一级。
int a = 10;
int* p1 = &a;// p1指向a
int** p2 = &p1;//p2指向p1
int &ref=a;
cout << "p1=" << p1 << " *p1=" << *p1;//p1存储a的地址,*p1获取a的值
cout << "p2=" << p2 << " *p2=" << *p2 ;p2存储p1的地址,*p2获取p1的值
运行结果
p1=0000009BADFDF7E8 *p1=10
p2=0000009BADFDF7E0 *p2=0000009BADFDF7E8
- sizeof(p1)=8,获取的是p1这个指针的大小。sizeof(ref)=4,获取的是a的大小。
- 当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但二者是独立变量,在函数中改变这个变量的指向不影响实参。而引用却可以,函数接收的是原变量的引用(别名),操作引用等同于操作原变量。
需要注意的小地方
- *的两种用途:在声明中用于表示指针类型。表达式中用于解引用指针,访问指针指向的值
- &的两种用途:在引用声明中使用,定义一个引用(别名)。在指针声明中使用,获取变量的内存地址
三、堆和栈
-
堆(Heap):用于运行时的动态内存分配,向上(高地址)生长。代码和数据区,在进程开始运行时就被指定了大小;而通过调用 malloc 和 free 这样的 C 标准库函数,可以让堆区动态地扩展和收缩。
-
用户栈(Stack):位于虚拟地址空间用户区顶部,向下(低地址)生长。一般用来存储局部变量和函数参数,结合堆栈指针可以方便地实现函数的调用和返回。
-
区别:
-
申请方式不同:栈由系统自动分配。堆是自己申请和释放的。
-
申请效率不同:
栈由系统分配,速度快,不会有碎片。
堆由程序员分配,速度慢,且会有碎片。