String的实现需要注意的是String的拷贝构造。它的拷贝构造有深拷贝和浅拷贝之分。

我们先来用浅拷贝实现String

classString{public:String(){str=newchar('A');}String(char*s){str=newchar[strlen(s)+1];if(str!=NULL){strcpy(str,s);}}String(constString&s){str=s.str;}String&operator=(constString&s){if(this!=&s){str=s.str;}return*this;}private:char*str;};voidtest(){Strings1("helloworld");Strings2(s1);}

当s1,s2出自的作用域时,会自动调用析构函数,此时s1,s2指向同一片内存。所以这块内存会被释放两次,程序会崩溃。


所以在这里我们要采用深拷贝的方式


构造函数和赋值运算符重载

String(constString&s){str=newchar[strlen(s.str)+1];//new出来一块新的空间if(str){strcpy(str,s.str);}}String&operator=(constString&s){if(this!=&s){if(str!=NULL){delete[]str;str=newchar[strlen(s.str)+1];strcpy(str,s.str);}}return*this;}

还有一种方法可以解决这个一块空间会被多次释放的问题,那就是写时拷贝

在第一次构造一个对象的时候,多开辟四个字节当做计数器,用来记录有几个指针指向这块空间。每当用这块空间拷贝构造一个新对象或者把这块空间赋给另外一个对象时,计数器相应增加。那么当调用析构函数时,每次计数器减一,当计数器减到一时,说明只有一个指针指向这块空间,此时再把这块空间delete,就不会出现一块空间多次释放的问题了。

classString{public:String(char*str=""):_str(newchar[strlen(str)+1+4]){*((int*)_str)=1;//将开辟的空间前4个字节强制转换成整型并赋为1_str=_str+4;//将_str重新定为真正字符串开始的地方,这样比较方便strcpy(_str,str);//不用每次向后找}String(constString&str){_str=str._str;(*((int*)(_str-4)))++;}~String(){if(*_str!=NULL){if(((*((int*)(_str-4)))--)==0)//判断计数器是否减到0{delete[](_str-4);}}}public:String&operator=(constString&str){if(this!=&str){if(((*((int*)(_str-4)))--)==0){delete[](_str-4);}_str=str._str;(*(int*)(str._str-4))++;return*this;}}char&operator[](intindex){char*tmp=_str;if(((*(int*)(_str-4))-1)!=0){(*(int*)(_str-4))--;_str=newchar[strlen(_str)+5];(*(int*)(_str+4))=1;_str=_str-4;strcpy(_str,tmp);}return_str[index];}private:char*_str;};

但是这样做也有一个坏处。就是指向同一块空间的指针,只要改一个指针指向的内容,等于其他的指针指向的内容也跟着改变了。