看的侯捷老师的课
用的string作为讲解
以上是老师给出了简单的string实现,
String s3(s1); //这里s3第一次出现,是拷贝构造
...
s3 = s2; //这里s3要被赋值,调用的是不一样的函数
复数也存在拷贝构造和赋值,但是之前的课没有讲到,因为编译器会自动一个bit一个bit这样的copy,复数只有实部和虚部,没有指针,所以默认的够用。
但string类的包含有指针,编译器默认的则不够用,默认的话,拷贝的对象和原对象,它们的指针会指向相同的地址,这不是我们想要的结果。
括号内为自己写的构造函数,拷贝构造函数等,
其中
String(const char* cstr = 0); //构造
String(const String& str); //拷贝构造
String& operator=(const String& str);//拷贝赋值
~String(); //析构
构造函数,默认指针为空指针,初始化时,若未指定值,则给*m_data一个结束符号,
若给定初值,strlen(cstr)给的值是不包含'\0'的长度,因此分配长度加一,用来放置结束符。
析构函数,离开作用域后会调用的函数。
因为使用new做了动态分配,分配了一块内存给“hello\0”存储,所以要delete将这块内存释放掉
类如果有指针成员,则一定要有拷贝构造和拷贝赋值!
没有的话,下图很好的体现了问题
"World\0"没有指针指向它,内存泄露了,
而"Hello\0"两个指针指向它,则一个改动,另一个也受影响。alias 别名,危险操作。
这种情况就是浅拷贝
我们要做的就是实现深拷贝
拷贝构造函数
传进参数,传入引用,且蓝本str不改动,所以加上const
拷贝构造函数的参数如果是普通传值类型,而非引用,会引发无限递归最终导致崩溃
这里引用知乎上看到的
复制构造函数,其实也是一个函数,如果你给它定义的参数,是一个类型,而不是一个类型的引用,它会首先调用该类型的复制构造函数,重新构造一个新的实例。
那么新的实例创建也会继续调用拷贝构造,则会无限递归。
拷贝构造要创建出足够的空间来存放蓝本str
String s1("hello ");
String s2(s1);
//String s2 = s1;
这里老师提到,当s2都是没出现时,两种方式起到的作用完全相同,都是调用拷贝构造函数。
拷贝赋值函数
要把右边的对象赋值到左边的对象上,则应该先清空左边的对象,再创建跟右边一样大的空间,然后把右边拷贝到左边。
自我赋值,会发生,但是有时候看起来不像,所以要检测。这样效率高,更关键是避免如下图情况
,保证了正确性。