浅谈类的复制构造函数和赋值运算符
下面的讨论都是建立在不使用复制省略的基础上
一、复制构造函数
搞清楚两个问题:什么时候会调用复制构造函数;复制构造函数的作用
1、何时调用复制构造函数
1.1 首先搞清楚,构造函数一般都是在初始化对象时,才会被调用;下面是几种简单的情况:假设a是一个已经存在的Vector对象
Vector b(a);
Vector c = a;
Vector d = Vector(a);
Vector* f = new Vector(a);
以上四种情况都会调用复制构造函数;对于第二行和第三行代码,有用到=,但是不一定会调用赋值运算符(如果编译器的实现方式是:首先使用复制构造函数创建一个临时变量,然后将该临时变量的值赋给新对象,这种情况就会调用赋值运算符),这去取决于编译器,我用的g++编译器并不会调用赋值运算符;
ps: =可以是赋值运算符,也可以是是初始化符号;参考:https://www.cnblogs.com/tengzijian/p/17964231
1.2 还有稍微复杂的情况,也将调用复制构造函数:
Vector a = Vector(1, 2);
Vector b = (1, 2);
上面两种情况,不仅会调用Vector(double, double)函数,还会隐式地调用Vector(Vector)复制构造函数;
对于按值传递的函数和返回值的函数,都会隐式地调用复制构造函数;
// 按引用传递
Vector & func(const Vector & v1, const Vector & v2)
{
v1.a = v1.a+v2.a;
v1.b = v1.a+v2.b;
return v1
}
// 按值传递
Vector func2(const Vector v1, const Vector v2)
{
return Vector(v1.a-v2.a, v1.b-v2.b);
}
对于第一种按引用传递的函数,并不会调用复制构造函数,因为并不涉及创建临时对象,也就是整个过程并没有创建新的对象,所以并不会调用复制构造函数;
对于第二种按值传递的函数,在传参过程中,会初始化形参,就和第一点提到的情况类似,所以也会调用复制构造函数;然后函数返回值,而不是引用,这里会显式地先调用Vector(double,double)构造函数创建一个让func2能够访问的Vector对象,然后返回语句引发对复制构造函数的隐式调用创建一个调用函数能够访问的对象;
复制省略:不会调用不必要的复制构造函数
2、复制构造函数的作用是什么:
默认的复制构造函数逐个复制非静态成员,复制的是成员的值,也就是浅复制,对于含有指针类型的成员数据,这种浅复制是不行的;
所以这种情况需要显式定义复制构造函数以适配需求;
二、赋值运算符
还是搞清楚两个问题,什么时候会调用,作用是什么
1、什么时候会调用赋值运算符
将一个已有的对象赋给另一个对象时,会调用赋值运算符;注意和初始化区别开;
Vector a = Vector(1, 2);
Vector b;
b = a;
2、赋值运算符的作用
默认的赋值运算符和默认的复制构造函数有着相同的问题;
所以对于含有指针数据成员的类,需要重载复制运算符;