C++内存管理
C++中,内存分成5个区。
栈:函数内局部变量;自动管理,效率高,但空间较小;
堆:new分配的内存块;手动管理,效率低,但空间大;
自由存储区:malloc分配的内存块;
静态/全局存储区:全局变量、静态变量;
常量存储区:常量。
new / delete 和 malloc / free
- 本质上:new和delete是操作符,malloc和free是库函数;
- 实现上:malloc将堆的特定段空间申请出来,new(1)调用operator new(C++的全局函数),(2)运行相应的构造函数以构造数据类型,并为其传入初值,(3)对象构造完成后,返回一个指向该数据类型的指针。对应的,delete也要先运行析构函数,再释放空间;
- 内存上:malloc申请的空间在堆上,new申请的空间在自由存储区(有些编译器new在底层的写法就是采用malloc来实现,所以也有一些说法说new申请的空间也在堆上,这也说明自由存储区可以在堆上,但不等同于堆)。
- 分配失败:new抛出异常,malloc返回NULL;
- 返回值类型:new不需要类型转换,malloc是void*,需要类型转换;
- 其它:new可以重载,malloc不能;new不支持已分配内存的扩张,malloc支持。
static
修饰变量:
- 放在静态存储区;
- 修饰全局变量:不能被其它文件所用,其它文件中可以定义相同名字的变量,不会发生冲突;
- 修饰局部变量:局部变量每次的值保持到下一次函数调用;
- 修饰类的成员变量:该变量与类绑定,而不再与实例绑定,所有该类的实例(包含子类的实例)公用该变量;
修饰函数:
- 修饰一般函数:静态函数不能被其它文件所用,其它文件中可以定义相同名字的函数,不会发生冲突;
- 修饰类的成员函数:
- 普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。静态成员函数不具有this指针,所以也无法访问属于类对象的非静态数据或函数,只能访问其它静态成员变量和函数;
- static成员函数不能被virtual修饰。
const
- 修饰一般变量:变量不可变,存储在常量区;
- 修饰指针:指针指向的内容不可变;
- 修饰类的成员变量:成员常量,不能修改,它只能在初始化列表中赋值;
- 修饰成员函数:实质上修饰的是成员函数影藏得this指针,表示该成员函数不能对类的成员变量做修改。
- 和define区别:
extern
- 修饰当前文件的全局变量:使得该全局变量在定义点之前就能被引用,使其作用域提前;
- 修饰其它文件的全局变量:想引用另外一个源文件中已经定义的外部变量。
多态和虚函数:
C++通过虚函数实现。只要含有虚函数的类就会为该类生成一个全局唯一虚函数表,虚函数表中记录类中虚函数的入口地址,同时会在每个实例内存的开始生成一个虚函数表指针,指向虚函数表。
而当一个子类继承一个有虚函数的父类后,同时也会继承父类的虚函数表,如果子类重写了一个虚函数,就会把虚函数表中该虚函数的地址改成重写后的版本。
这样一个父类指针指向一个子类对象,想要调用某虚函数时,通过对象内存地址找到的是子类虚函数表,就可以调用子类的版本。这就实现了多态,即一个父类指针指向不同对象,调用相同的函数,表现出不同的形态。
struct和union
- struct:成员占用的内存空间独立,大小是累加的;
- union:所有的成员共用一块地址空间。
引用和指针
- 引用:变量的别名;
- 指针:一个变量,存放地址。
野指针
指针变量没有被初始化;
指针被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针;
指针操作超越了变量的作用域范围。