class with pointer members

由于字符串的长度是不确度的,所以使用数组来存放字符串,不好确定数组大小

因此选择使用char* 动态分配内存的方式来存储

Big three 3个特殊函数

字符串string类的数据成员只有一个char* 指针,通过动态分配内存指向字符串

带有指针的类有三个特殊函数 析构函数 复制构造函数 operator =

不能使用编译器自动生成的operator =和复制构造函数,这些函数是直接的bit by bit 的指针赋值

会造成内存泄漏,即内容还在那里,没有被回收,但没有指针指向这里

同时会形成两个指针指向同一块内存区域,当一个指针delete后,另外一个也会受影响(alias)

复制构造函数和构造函数

首先获取参数的字符长度,由于strlen返回的长度不包括结尾的'\0',所以要分配的内存空间要strlen(cstr)+1

在常规的构造函数中,即使指针参数为空指针,也要分配一个char的内存空间,内容为'\0'

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';
}
}


调用strcpy复制,自动复制结尾的'\0'

inline
String::String(const String& str)
{
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}


析构函数

当string类的实例对象的作用域结束后,os会收回其所占用的内存空间

但string类的数据只有一个指针,这个指针所指向的内容是不会自动回收的,所以要在析构函数中手动delete

inline
String::~String()
{
delete[] m_data;
}


operator =

实现和复制构造函数类似,但是要先清空自身内容,再进行赋值

要判定自我赋值的情况,如果不判断,将直接清空自身的内容,指针成为野指针,会发生undefined behavior

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;
}


stack heap

在函数function body内声明的变量,内存来自于stack

heap是os提供的一块global内存空间,可通过new进行动态分配内存

stack object在作用域结束后被自动清理 而heap object仍然存在

static

在变量声明前加static关键字,即为static object,其在作用域结束后仍然存在,直到整个程序结束

global object也可看作一种static object,作用域是整个程序

new delete

new被编译器转化为三个步骤:

1.调用malloc分配内存

2.static_cast类型转换

3.调用构造函数

delete为两个步骤:

1.调用析构函数

2.调用free释放内存

array new 一定要搭配 array delete

delete 和 delete[] 都会将其所指的内存块全部释放

区别在于delete[]会对数组内的每一个元素调用析构函数 而delete只对第一个元素调用

如果数组元素为带有指针成员的类,使用delete会造成memory leak