class里带有指针,一定要关注:
- 拷贝构造函数
- 拷贝赋值函数
- 析构函数
实现带有指针的类,一定要重新实现拷贝构造和拷贝赋值函数!如果使用默认拷贝就会有内存泄漏的风险。
String a("hello");
String b("world");
b = a; // 使用默认拷贝就会内存泄漏,b不再指向原先内存
ctor 和 dtor
inline String::String(const char* cstr) {
if (cstr) { // 如果指定了初值
m_data = new char[strlen(cstr)+1];
strcpy_s(m_data, strlen(cstr) + 1, cstr); // 这里要使用strcpy_s, strcpy有安全风险
}
else { // 未指定初值
m_data = new char[1];
*m_data = '\0';
}
}
inline String::~String(){
delete[] m_data; // 清理内存,释放动态分配的内存
}
copy ctor
inline String::String(const String& str) {
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy_s(m_data, strlen(str.m_data) + 1, str.m_data);
}
{
String s1("hello");
String s2(s1); // 直接取另一个object的private data, (兄弟之间互为友元)
}
copy op=
inline String& String::operator=(const String& str) {
if (this == &str) // self assignment检测自我赋值
return *this;
delete[] m_data; // 释放内存
m_data = new char[ strlen(str.m_data) + 1 ]; // 分配内存
strcpy_s(m_data, strlen(str.m_data) + 1, str.m_data); // 赋值数据
return *this; // this指针就是指向调用者本身
}
上面代码中的检测自我赋值部分一定不能省略,非常重要! 如果两个指针指向同一块内存,operator= 第一步就是delete, 就会造成指针访问不存在的内存,产生不确定行为。
new 和 delete
stack, heap, global对象的生命周期
stack中的对象会自动调用dtor, 不需要手动delete;
new动态分配的是heap内存;new得的内存就有责任delete掉,释放内存。
new:先分配内存,再调用ctor
delete: 先调用dtor,再释放内存
array new 一定要搭配array delete
比较下面两种delete方式的差别
String *p = new String[3];
// 1 delete p;
// 2 delete[] p;
方式1是不正确的用法,只唤起1次dtor,会造成内存泄漏;
方式2在delete后面加上[]
相当于告诉编译器删除的是array
new []
搭配 delete[]