1 | class Test{ |
为什么拷贝构造函数不能写成1的形式,必须写成2 的形式?
之前的回答是 减少一次拷贝,因为传值的时候会调用拷贝构造函数,传引用可以避免掉,至于传const引用,主要是避免实参被修改。
但现在深入的再思考就会发现,传值的时候调用拷贝构造函数,但实际上这是一个无限套娃,调用拷贝构造函数的时候,传入参数也是传值,这又要调用拷贝构造函数。
循环往复,没有尽头了。
至于3和4的区别在哪里呢?
3和4是两种写法。
用写法3的时候,就是operator=的实现就是拷贝构造函数的另一种实现,唯一的区别是要多一步检查(检查= 两边的对象是否相同,如果相同的话不做任何操作,否则,就要释放掉当前的内存,申请新内存,并拷贝内容过来)。
写法4的时候,就是我的申请内存的操作都是放在拷贝构造函数里的,这样我们的operator=的实现就很简单了,因为构造的临时变量已经分配了内存,那么代码中只需要进行swap就好了。 而且在swap的时候,我们交换的是临时变量和this的内容,离开当前堆栈销毁临时变量的时候,释放的也是我们原先this指向的this的内容,还减少了我们释放旧内存的操作。
4相比于3的写法,个人认为最大的好处是确保了 the strong guarantee。因为operator=如果不调用拷贝构造函数的话,也要自己在内部申请内存,拷贝内容,而且内部申请内存 抛出异常的话,还要确保代码满足the strong gurantee。而在拷贝构造函数中申请内存的话,其实也要处理异常,不过这就保证operator=是nothrow的。
既然拷贝构造函数本身就有异常处理,那我们何苦在operator=中也要再加上抛出exception的风险呢。 而且swap本身也是nothrow的。
至于3和4的返回值都是 引用类型。如果我们返回的是普通值类型,那么返回值这里要调用拷贝构造函数,这里就会造成内存泄漏,就拿4来说,我们本身就调用拷贝构造生成了一个临时变量(虽然是临时变量,但内存不会被销毁)
下面是我们的一个例子
1 | class TestArray{ |
1 的输出 应该是 TestArray这没有疑问
2 的输出是 Copy Constructor 也没问题
3 的输出,照着我最初的理解,是TestArray c; c = a; 那么输出应该是TestArray Copy Constructor 26 destructor,但最终的输出是 Copy Constructor。
这是什么原因呢? 在编译器看来,不会做这么麻烦的操作,因为c没有被构造,所以这里TestArray c = a 是初始化而不是赋值,可以被看作TestArray c(a)。这样理解的话,就能理解为什么3的输出只有一个copy constructor了。
4 的输出 是TestArray
5 =左边d是已经被初始化的值,所以,此时才适用于 赋值运算符,此时在调用=的时候,传值会导致调用copy constructor。所以输出是copy constructor 26。此时,swap之后,临时变量要被释放,此时会调用destructor。
接着在离开main函数的时候,依次调用d c b a的析构函数。
那么最终的输出是
1 | TestArray |