string类是带指针的类,需要进行动态内存分配,需要考虑字符串最后一个字符用'\0'表示结束,需要用到c语言处理字符串的一些函数,以及需要考虑赋值和移动相关函数本身的特性,所以一些基础函数的定义要特别注意,很考验功底,注意构造函数和析构函数一般不设置为inline,具体原因见Effective C++条款30(避免潜在的代码膨胀)
0. 类函数声明与数据成员
class String {
public:
String(const char *cstr = 0); //const的正确使用,默认参数
String(const String &str); //参数格式
String& operator=(const String &str); //返回值,操作符函数写法,参数格式
~String();
String(String &&str) noexcept ; //移动构造函数参数格式,不抛出异常,声明和定义都要写,写在初始化列表前面
String& operator=(String &&str) noexcept ; //返回类型,赋值运算符函数写法,参数类型,不抛出异常
private:
char *m_data; //底层数据存储形式,私有的
};
1. 普通构造函数
String::String(const char *cstr) { //无返回值,定义不写默认参数
if (cstr) { //注意空字符串的情况
m_data = new char[strlen(cstr) + 1]; //求c语言格式字符串长度用strlen,动态分配内存
strcpy(m_data, cstr); //拷贝c语言字符串用strcpy,目标参数在前
}
else {
m_data = new char[1];
*m_data = '\0'; //字符串以'\0'字符结尾
}
}
2. 拷贝构造函数
String::String(const String &str) {
m_data = new char[strlen(str.m_data) + 1]; //动态分配内存,求c语言字符串长度用strlen,字符串以'\0'字符结尾
strcpy(m_data, str.m_data); //c语言字符串拷贝用strcpy
}
3. 拷贝赋值运算符
String& String::operator=(const String &str) {
if (this == &str) return *this; //避免自我赋值
delete[] m_data; //先释放=左边的字符串的数据指针
m_data = new char[strlen(str.m_data) + 1]; //动态分配内存,求c语言字符串长度用strlen,字符串以'\0'字符结尾
strcpy(m_data, str.m_data); //c语言字符串拷贝
return *this; //返回引用
}
4. 析构函数
String::~String() {
delete[] m_data; //释放内存
m_data = nullptr; //指针置空
}
5. 移动构造函数
String::String(String &&str) noexcept { //承诺不抛出异常
m_data = str.m_data;
str.m_data = nullptr; //原来的数据指针置空
}
移动的两个函数都不抛出异常
6. 移动赋值运算符
String& String::operator=(String &&str) noexcept { //承诺不抛出异常
if (this == &str) return *this; //避免自我赋值
delete[] m_data; //释放内存,注意[]
m_data = str.m_data; //直接拷贝指针,不需要重新分配内存
str.m_data = nullptr; //原指针置空
return *this; //返回引用
}
移动的两个函数都不抛出异常
7. 完整代码
#include <cstring>
class String {
public:
String(const char *cstr = 0); //const的正确使用,默认参数
String(const String &str); //参数格式
String& operator=(const String &str); //返回值,操作符函数写法,参数格式
~String();
String(String &&str) noexcept ; //移动构造函数参数格式,不抛出异常,声明和定义都要写,写在初始化列表前面
String& operator=(String &&str) noexcept ; //返回类型,赋值运算符函数写法,参数类型,不抛出异常
private:
char *m_data; //底层数据存储形式,私有的
};
String::String(const char *cstr) { //无返回值,定义不写默认参数
if (cstr) { //注意空字符串的情况
m_data = new char[strlen(cstr) + 1]; //求c语言格式字符串长度用strlen,动态分配内存
strcpy(m_data, cstr); //拷贝c语言字符串用strcpy,目标参数在前
}
else {
m_data = new char[1];
*m_data = '\0'; //字符串以'\0'字符结尾
}
}
String::String(const String &str) {
m_data = new char[strlen(str.m_data) + 1]; //动态分配内存,求c语言字符串长度用strlen,字符串以'\0'字符结尾
strcpy(m_data, str.m_data); //c语言字符串拷贝用strcpy
}
String& String::operator=(const String &str) {
if (this == &str) return *this; //避免自我赋值
delete[] m_data; //先释放=左边的字符串的数据指针
m_data = new char[strlen(str.m_data) + 1]; //动态分配内存,求c语言字符串长度用strlen,字符串以'\0'字符结尾
strcpy(m_data, str.m_data); //c语言字符串拷贝
return *this; //返回引用
}
String::~String() {
delete[] m_data; //释放内存
m_data = nullptr; //指针置空
}
String::String(String &&str) noexcept { //承诺不抛出异常
m_data = str.m_data;
str.m_data = nullptr; //原来的数据指针置空
}
String& String::operator=(String &&str) noexcept { //承诺不抛出异常
if (this == &str) return *this; //避免自我赋值
delete[] m_data; //释放内存,注意[]
m_data = str.m_data; //直接拷贝指针,不需要重新分配内存
str.m_data = nullptr; //原指针置空
return *this; //返回引用
}