0 说明
本文仅实现string类中几种重要、常用的功能,旨在掌握C++类设计的关键知识点。
1 知识点
- 构造函数(包括默认构造函数、拷贝构造函数以及自定义的其他构造函数)
- 运算符重载
- 友元
- 析构函数
- C风格字符的操作
2 类的设计
为使代码简洁,本文采取分文件编写模式,类的声明包含在头文件"mystring.h"中,成员的实现包含在源文件"mystring.h"中,"main.cpp"文件是若干测试。
2.1 构造函数
类比C++中原有的string类,我们可以定义一个string对象(不指定初始值),可以用一个已存在的string对象初始化一个新定义的对象,也可以用一个C风格字符串给一个string对象赋值。为了实现同样的功能,自定义的String类需要如下几种构造函数:
- 默认构造函数:String(),用于直接构建一个不指定初值的对象
- 拷贝构造函数:String(const String& s),用于初始化新的对象
- 普通有参构造函数:String(const String* s),用于以C风格字符串初始化一个对象
2.2 重载函数
由于String类是我们自定义的,所以有必要重载若干常用运算符以丰富String类所支持的功能。具体地,本文将重载一下运算符:
- = 运算符,=运算符给已存在的对象赋值,我们将重载以String对象赋值和以C风格字符串赋值两种形式
- == 运算符,用于判断两个String对象是否相同
- > 运算符,判断一个对象是否大于另一个对象
- < 运算符,判断一个对象是否小于另一个对象
- += 运算符,将两个对象拼接到一起
- [] 运算符,访问字符串某个位置的元素,分为常量和非常量两个版本重载
- << 运算符,输出一个String对象
- >> 运算符,实现读入一个字符流到String对象
2.3 普通成员函数
只设计一个length()函数用于获取字符串的长度。
2.4 析构函数
析构函数无法重载较为简单,一个类中只能有一个析构函数,后续介绍析构函数的内部实现,这里不做过多阐述。
2.5 成员变量
只设计一个变量len用于记录字符串的长度。
类的设计部分代码如下:
1 #ifndef MYSTRING_H_INCLUDED 2 #define MYSTRING_H_INCLUDED 3 4 #include <iostream> 5 6 using namespace std; 7 8 class String { 9 public: 10 // constructor function 11 String(); // default constructor function 12 String(const String& s); // copy constructor function 13 String(const char* s); 14 15 // overload function 16 String& operator=(const String& s); // 17 String& operator=(const char* s); 18 String& operator+=(const String& s); 19 friend int operator==(const String& s1, const String& s2); 20 friend int operator>(const String& s1, const String& s2); 21 friend int operator<(const String& s1, const String& s2); 22 char& operator[](int i); 23 const char& operator[](int i) const; 24 friend ostream& operator<<(ostream& os, String& s); 25 friend istream& operator>>(istream& is, String& s); 26 27 // common function 28 int length() const; 29 30 // 31 ~String(); 32 33 private: 34 char* str; 35 int len; 36 }; 37 38 39 40 #endif // MYSTRING_H_INCLUDED
3 成员设计
3.1 构造函数
- 默认构造函数:String()
将默认值设置为空,因此只需申请一个char大小的内存空间用于存放C风格字符串结尾标志 ‘\0’。
1 String::String() { 2 len = 0; 3 str = new char[1]; 4 str[0] = '\0'; 5 }
- 拷贝构造函数:String(const String& s)
首先判断s是否为空,然后更新新字符串的长度,接着开辟一段内存空间,最后用strcpy()函数将s.str复制到新的字符串中。
1 String::String(const String& s) { 2 if (s.length() == 0) { 3 String(); 4 } 5 len = s.len; 6 str = new char[len + 1]; 7 strcpy(str, s.str); 8 }
-
普通有参构造函数:String(const String* s)
与拷贝构造函数类似。
1 String::String(const char* s) { 2 if (s == nullptr) { 3 String(); 4 } 5 len = strlen(s); 6 str = new char[len + 1]; 7 strcpy(str, s); 8
3.2 运算符重载
- 赋值运算符(类对象版):=
先判断等号两侧对象是否相同。释放原对象内存是关键。
1 String& String::operator=(const String& s) { 2 if (this == &s) { 3 return *this; 4 } 5 6 delete []str; 7 int len = s.length(); 8 str = new char[len + 1]; 9 strcpy(str, s.str); 10 11 return *this; 12 }
- 赋值运算符(C风格字符串版):=
1 String& String::operator=(const char* s) { 2 delete []str; 3 len = strlen(s); 4 str = new char[len + 1]; 5 strcpy(str, s); 6 7 return *this; 8 }
- 重载运算符:+=
采用成员函数的方式重载,只需传递一个参数。将运算符左侧对象复制一份到新对象c中,然后删除运算符左侧对象原有的的内存空间,再扩容、字符拷贝、释放临时对象c。
1 String& String::operator+=(const String& s) { 2 String c(*this); 3 delete []this->str; 4 this->str = new char[this->length() + s.length()]; 5 stpcpy(this->str, c.str); 6 stpcpy(this->str, s.str); 7 this->len += s.length(); 8 delete []c.str; 9 10 return *this; 11 }
- 重载比较运算符:==、<,>
1 int operator==(const String& s1, const String& s2) { 2 return (strcmp(s1.str, s2.str) == 0); 3 } 4 5 int operator>(const String& s1, const String& s2) { 6 return (strcmp(s1.str, s2.str) > 0); 7 } 8 9 int operator<(const String& s1, const String& s2) { 10 return (strcmp(s1.str, s2.str) < 0); 11 }
- 重载运算符:[]
1 char& String::operator[](int i) { 2 return str[i]; 3 } 4 const char& String::operator[](int i) const { 5 return str[i]; 6 }
- 重载输入输出运算符:>>、<<
1 ostream & operator<<(ostream& os, String& s) { 2 os << s.str; 3 return os; 4 } 5 6 istream& operator>>(istream& is, String& s){ 7 char temp[80]; 8 cin.getline(temp, 80); 9 //is >> temp; 10 s = temp; 11 12 return is; 13 }
3.3 普通成员函数
- 获取字符串长度:length()
1 int String::length() { 2 return len; 3 }
3.4 析构函数
1 String::~String() { 2 delete []str; 3 }
4 测试
1 int main() 2 { 3 String str1("abc"); 4 String str2(str1); 5 String str3; 6 str3 = str2; 7 cout << "str1的长度: " << str1.length() << endl; 8 cout << "str1[0] = " << str1[0] << endl; 9 cout << "str1 = " << str1 << endl; 10 cout << "str3 = " << str3 << endl; 11 cin >> str3; 12 cout << "str3 = " << str3 << endl; 13 cout << "str3的长度: " << str3.length() << endl; 14 return 0; 15 }