编写一个带指针成员变量的类
1. 拷贝函数
- 当类中有指针成员变量时,必须要实现拷贝函数,不能使用系统提供的默认方法。
2. Big Three
- 拷贝构造
- 拷贝赋值
- 析构函数
字符串的两种存储形式:最前存贮字符串的长度;最后有结束符'\0'
2.1 拷贝构造函数
- 构造函数的参数类型为本class
- new一份同样大小的内存,再使用strcpy
大小要+1,为结束位的'\0'留出位置
2.1 拷贝赋值函数
- 重载运算符=
- 监测是否自我赋值。之后先清空,在申请同样大小的内存,再拷贝strcopy
如果没有做自我赋值检测,会出现把自己清空后再赋值,这时调用指针会出现不确定结果
3. 堆栈
- 堆,heap。由操作系统提供的一块global内存空间,程序可动态分配。通过 new去获得。malloc
- 局部的指针变量如果没有被delete,那么指针变量的堆空间将无法被安全释放,造成了内存泄漏。
- new的流程:先分配memory,再调用构造函数。
void* mem = operator new( sizeof(Complex) ); //分配内存,调用malloc(sizeof())
pc = static_cast<Complex*>(mem); //类型转换
pc->Complex::Complex(1,2); //构造函数
- 堆,heap
- delete的流程:先调用析构函数,再释放memory
String::~String(ps); //析构函数
operator delete(ps); //释放内存,内部调用free
- 栈,stack。存在于作用域内的一块内存空间。
- 局部对象,会被自动清理。
- static关键字。静态对象的生命周期和程序绑定,和作用域无关。
- 全局对象。
4. 内存中new时的真正形态
4.1 单个对象时
- debug模式
- 每申请一个对象,内存中的实际大小为:
- 对象本身的内存大小
- 包裹对象的内存32字节+4字节
- cookie内存4+4,在整个内存块的头和尾
- 全部大小相加后,再向16的倍数对齐
- 每申请一个对象,内存中的实际大小为:
- release模式
- 每申请一个对象,内存中的实际大小为:
- 对象本身的内存大小
- cookie内存4+4,在整个内存块的头和尾
- 全部大小相加后,再向16的倍数对齐
- 每申请一个对象,内存中的实际大小为:
- cookie内存的作用
- 标记回收时的内存大小
- 因为一定是16的倍数,所以最后一位一定是0,可以利用最后一位的0/1来标示当前内存块是给出状态还是回收状态
4.2 数组对象时
- new type[] 要搭配 delete []
- debug模式
- 每申请一个对象,内存中的实际大小为:
- 记录数组大小:4字节
- 数组长度个的对象本身的内存大小:n * object
- 包裹整个数组和大小的内存32字节+4字节
- cookie内存4+4,在整个内存块的头和尾
- 全部大小相加后,再向16的倍数对齐
- 每申请一个对象,内存中的实际大小为:
- release模式
- 每申请一个对象,内存中的实际大小为:
- 记录数组大小:4字节
- 数组长度个的对象本身的内存大小:n * object
- cookie内存4+4,在整个内存块的头和尾
- 全部大小相加后,再向16的倍数对齐
- 每申请一个对象,内存中的实际大小为:
5. 类模版
template<typename T>
class complex
{
public:
complex (T r = 0, T i = 0)
: re (r), im (i)
{}
private:
T re, im;
}
6. 函数模版
template <class T>
inline
const T& min(const T& a, const T& b)
{
return b < a ? b : a;
}
思考
1. new type[] 和 delete [] 成对出现的原因?
- 如果不成对出现,会造成内存泄漏,但是泄漏的内存并不是数组中对象的内存,数组中的所有对象已经被正确释放。delete[]的作用是,通知编译器释放的是个数组对象,然后调用多次的析构函数;如果只是调用delete,只会调用一次析构函数。
- 所以真正内存泄漏的内存,是数组对象中指针所指向的那些内存。也就是说如果当数组对象的内部变量没有指针时,是不会出现问题。
- 要保持良好的变成习惯,array new 和 array delete要成对出现。
2. ' * '和' & '标志符在不同位置时的不同含义
- ' & '在类型名后' typename& ',表示为引用;' & '在对象前' &object ',表示为取地址
3. 回答第一周笔记中的第一个思考问题,为什么可以数据需要单独存储,而函数可以公用一份?
- 通过对象调用类方法时的真相
complex c1,c2;
cout << c1.real(); // 实际情况,cout << complex::real(&c1);
cout << c2.real(); // 实际情况,cout << complex::real(&c2);
4. 为什么静态函数只能使用静态数据,而不能使用直接使用类的私有数据?
- 因为static函数的参数列表中,没有默认的this指针。
- 静态函数可以通过类名调用,也可以使用对象调用。
5. 类模版和函数模版的使用区别
- 类模版使用时必须明确指出模版的类型;函数模版使用时,不用明确指出,编译器会进行argument deduction,参数推导