【8】三大函数:拷贝构造、拷贝赋值、析构
如果class中有指针,则不能用编译器给的默认拷贝,需要自己写构造函数来拷贝。
class中有指针,多半是要做动态分配,则一定要有析构函数,delete字符串
以下public里面从上往下,分别是构造函数、拷贝构造函数、拷贝赋值、析构函数。最后一个是一个成员函数,传回指针,不改变data,所以要加上const。
字符串有多长,有两种设计思路,一种是在字符串最后有个结束符号,另一种是在字符串定义初规定字符串的长度。
有构造函数,就要有对应的析构函数,释放内存。析构函数会被自动调用
class里有指针,多半是要做动态分配的。
如果有指针的成员函数,但是没有对应的构造函数和析构函数。如下图所示,叫做浅拷贝,把a值赋给b,此时只是b指针与a指针指向相同,b指针原本的内存储存就没有办法找到了,发生了内存泄漏。
拷贝构造函数,传入的参数就是它自己那个类型。函数,首先创造一个足够的空间来放蓝本。
String::String(const String& str)
{
m_data = new char[strlen(str.n_data)+1]; //分配空间
strcpy(m_data, str.m_data); //拷贝内容
}
拷贝赋值函数,需要将原来的空间清空、再分配内存、再拷贝过来。
- 清空自身
- 分配足够的内存空间
- 拷贝内容
检测自我赋值,为了防止错误
output函数
操作符重载,定义<<输出符号。
堆、栈、内存管理
- stack object的生命期:在作用域结束时就被清理。scope结束时,析构函数被自动调用
- static local objects的生命期:
static Complex c2(1,2);
生命在作用域结束后仍存在,直到程序结束。 - global objects :写在全局作用域之中(任何大括号之外的), 整个程序结束之后生命结束;
heap objects 的生命期:创建一个heap 对象,必须要delete该对象,否则会出现内存泄漏,即指针所指的heap object仍然存在,但指针p的生命却结束了,作用域之外再也见不到指针,也就没有就会delete 该指针。
new一个对象的顺序,拆分成三个动作:
- 使用operator new函数,内部就是调用c中的malloc()函数,分配内存的作用。
- 把complex类型转化为void
- 利用指针调用构造函数
delete一个对象的顺序 - 先调用构造函数
- 再释放内存
以下每一个小空格是4个byte,complex类有两个double数值,所以分配8个byte,在调试模式下,绿色的内存空间上面多得到8x4个byte,下面多得到1x4个byte. 最上和最下的内存区域(红色)各分配4个byte,叫做cookie。
在VC中给的每一个内存块都是16的倍数,所以需要填补一些pad。
如果是在release模式下,就不需要debug head(灰色部分)。只需要上下cookie,是用来记录上下内存的大小,用于后面回收这段内存。
字符串的大小,内部只含一根指针,大小是4个byte。
下面表示用array new的分配内存的方式。
array new一定要搭配array delete
string.h
using namespace std;
#include <iostream>
using namespace std;
class String //设计字符串,用指针可以实现动态分配,不用具体规定字符串的长度
{
public:
//构造函数和拷贝构造函数没有返回类型
String(const char* cstr = 0); //构造函数
String(const String& str); //拷贝构造函数,传进来的东西不会改变,所以const
String& operator=(const String& str); //拷贝赋值函数,返回引用,因为本身就是存在的,不需要去创建
//一般考虑传回的是reference,传回的东西存放的地方只要不是local object,就可以传回reference
char* get_c_str() const{ return m_data; }
private:
char* m_data; //class中有pointer member 所以需要考虑big three。
};
inline
String::String(const char* cstr = 0) //构造函数
{
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
}
else { // 未指定初值
m_data = new char[1]; *m_data = '\0';
}
}
inline
String::~String() //析构函数
{
delete[] m_data; //array new要用 array delete
}
inline
String::String(const String& str) //拷贝构造函数
{
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}
inline
String& String::operator=(const String& str) //拷贝赋值函数
{
if (this == &str)
return *this; //判断是否是自我赋值
delete[] m_data;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this; //*this是传出去的东西,接收的是返回的东西String&,传出去的东西不用管接收方的返回类型
}