String的实现需要注意的是String的拷贝构造。它的拷贝构造有深拷贝和浅拷贝之分。
建网站原本是网站策划师、网络程序员、网页设计师等,应用各种网络程序开发技术和网页设计技术配合操作的协同工作。成都创新互联公司专业提供成都网站设计、成都网站建设、外贸网站建设,网页设计,网站制作(企业站、响应式网站建设、电商门户网站)等服务,从网站深度策划、搜索引擎友好度优化到用户体验的提升,我们力求做到极致!
我们先来用浅拷贝实现String
class String { public: String() { str = new char('A'); } String(char *s) { str = new char[strlen(s) + 1]; if (str != NULL) { strcpy(str, s); } } String(const String& s) { str=s.str; } String& operator=(const String& s) { if (this != &s) { str=s.str; } return *this; } private: char *str; }; void test() { String s1("hello world"); String s2(s1); }
当s1,s2出自的作用域时,会自动调用析构函数,此时s1,s2指向同一片内存。所以这块内存会被释放两次,程序会崩溃。
所以在这里我们要采用深拷贝的方式
构造函数和赋值运算符重载
String(const String& s) { str = new char[strlen(s.str) + 1]; //new出来一块新的空间 if (str) { strcpy(str, s.str); } } String& operator=(const String& s) { if (this != &s) { if (str != NULL) { delete[] str; str = new char[strlen(s.str) + 1]; strcpy(str, s.str); } } return *this; }
还有一种方法可以解决这个一块空间会被多次释放的问题,那就是写时拷贝
在第一次构造一个对象的时候,多开辟四个字节当做计数器,用来记录有几个指针指向这块空间。每当用这块空间拷贝构造一个新对象或者把这块空间赋给另外一个对象时,计数器相应增加。那么当调用析构函数时,每次计数器减一,当计数器减到一时,说明只有一个指针指向这块空间,此时再把这块空间delete,就不会出现一块空间多次释放的问题了。
class String { public: String(char *str="") :_str(new char[strlen(str)+1+4]) { *((int *)_str) = 1; //将开辟的空间前4个字节强制转换成整型并赋为1 _str = _str + 4; //将_str重新定为真正字符串开始的地方,这样比较方便 strcpy(_str, str); //不用每次向后找 } String(const String& str) { _str = str._str; (*((int *)(_str - 4)))++; } ~String() { if (*_str != NULL) { if (((*((int *)(_str - 4)))--) == 0) //判断计数器是否减到0 { delete[] (_str-4); } } } public: String& operator=(const String& str) { if (this != &str) { if (((*((int *)(_str - 4)))--) == 0) { delete[] (_str-4); } _str = str._str; (*(int *)(str._str - 4))++; return *this; } } char& operator[](int index) { char *tmp = _str; if (((*(int *)(_str - 4))-1) != 0) { (*(int *)(_str - 4))--; _str = new char[strlen(_str) + 5]; (*(int *)(_str + 4)) = 1; _str = _str - 4; strcpy(_str, tmp); } return _str[index]; } private: char *_str; };
但是这样做也有一个坏处。就是指向同一块空间的指针,只要改一个指针指向的内容,等于其他的指针指向的内容也跟着改变了。