c++复制构造函数

基本概念

复制构造函数(Copy constructor)是c++中的一个特殊构造函数,也称拷贝构造函数,它只有一个参数,参数类型为同类对象的引用。

如果没有定义复制构造函数,那么编译器将生成默认的复制构造函数。默认的复制构造函数完成复制的功能。

复制构造函数的参数为同类对象的引用,可以是常应用,也可以是非常引用。形如类名::类名(类名&)类名::类名(const 类名&)

默认复制构造函数

class Complex {//复数类
public:
    double real, imag;//分别表示实部以及虚部
public:
    //构造函数
    Complex(double r,double i) {
        real = r;
        imag = i;
    }
};

对于上面的复数类,假设我们有如下的初始化:

int main() {
    Complex c1(5, 2);
    cout << "c1.real=" << c1.real << "\tc1.imag=" << c1.imag << endl;
    Complex c2(c1);//调用复制构造函数初始化c2
    cout << "c2.real=" << c2.real << "\tc2.imag=" << c2.imag << endl;
    return 0;
}

在上面的初始化工作中,我们调用构造函数对c1进行初始化工作,然后输出c1的实部以及虚部。然后用c1去初始化c2,此时将调用编译器自动生成的构造函数将c2初始化为和c1一样,然后输出c2的实部以及虚部。运行上面的程序,得到如下的输出:

c1.real=5       c1.imag=2
c2.real=5       c2.imag=2

可以看到此时c2被初始化为和c1相同。

编写复制构造函数

在上面的例子中,我们并没有编写复制构造函数,此时编译器将生成一个默认的复制构造函数完成复制工作。

当我们自己编写了复制构造函数之后,编译器将不再生成默认的复制构造函数。如下面的例子所示:

class Complex {//复数类
public:
    double real, imag;//分别表示实部以及虚部
    //构造函数
    Complex(double r,double i) {
        real = r;
        imag = i;
    }
    Complex(const Complex& c) {//复制构造函数
        real = c.real;
        imag = c.imag;
        cout << "调用复制构造函数!!" << endl;
    }
};

对于上面的类,在复制构造函数被调用的时候将完成复制的工作,并且向控制台输出调用复制构造函数!!,假设我们有如下的初始化:

int main() {
    Complex c1(5, 2);
    cout << "c1.real=" << c1.real << "\tc1.imag=" << c1.imag << endl;
    Complex c2(c1);//调用复制构造函数初始化c2
    cout << "c2.real=" << c2.real << "\tc2.imag=" << c2.imag << endl;
    return 0;
}

执行后可以得到如下的结果:

c1.real=5       c1.imag=2
调用复制构造函数!!
c2.real=5       c2.imag=2

可以看到,此时我们自己编写的复制构造函数被调用,并且向屏幕输出了调用复制构造函数!!

错误用法

复制构造函数的参数一定要是对同类对象的引用,不能为其它的。

class Complex {
public:
    double real, imag;
    Complex(double r,double i) {
        real = r;
        imag = i;
    }
    Complex(Complex c) {//错误的写法
        real = c.real;
        imag = c.imag;
        cout << "调用复制构造函数!!" << endl;
    }
};

如上所示,我们错误的将参数Complex& c写为了Complex c,此时我们的编译将无法通过,在visual studio 2019中将得到以下的报错:

error C2652: “Complex”: 非法的复制构造函数: 第一个参数不应是“Complex”
message : 参见“Complex”的声明
error C2333: “Complex::Complex”: 函数声明中有错误;跳过函数体
error C2558: class“Complex”: 没有可用的复制构造函数或复制构造函数声明为“explicit”

总之,复制构造函数的参数一定要是同类对象的引用

复制构造函数起作用的情况

用一个对象来初始化正在构造的对象变量

如上面编写复制构造函数小节中所展示的那样,当我们用同类的一个对象去初始化另一个对象时,会导致构造函数被调用。

需要注意的是:

Complex c1(5, 2);
Complex c2(c1);
Complex c3 = c1;//初始化语句,非赋值语句

上面的第三天语句为初始化语句,并非赋值语句。

函数参数作为对象传值

如果某函数有一个参数是类的对象,那么该函数被调用时,该类的复制构造函数将被调用。

class Complex {//复数类
public:
    Complex() {};
    Complex(const Complex& c) {//复制构造函数
        cout << "调用复制构造函数!!" << endl;
    }
};

对于上面的类,假设我们有如下调用:

void fun(Complex c){}
int main() {
    Complex c;
    fun(c);//调用fun(Complex c)
    return 0;
}

其中我们定义了一个函数fun(Complex c),其参数为一个Complex对象,当我们在调用fun(Complex c)的时候,形参c将被初始化,此时将调用复制构造函数完成初始化工作,产生如下的输出:

调用复制构造函数!!

需要注意的是,如果函数的参数为对象的引用或常引用时,将不会导致构造函数被调用,如下所示:

void fun(Complex &c){}

函数返回一个对象

如果函数的返回值是某类的对象,则函数返回时,复制构造函数将被调用。

class Complex {//复数类
public:
    Complex() {};
    Complex(const Complex& c) {//复制构造函数
        cout << "调用复制构造函数!!" << endl;
    }
};

对于上面的类有如下调用:

Complex fun(){
    Complex c(2,3);
    return c;
}
int main() {
    cout << fun().real << endl;
    return 0;
}

函数fun()返回一个Complex类的对象,在调用fun()函数时会导致复制构造函数被调用。

注意事项

  • 上述情形未必会调用复制构造函数。因为C++标准允许编译器实现做一些优化。例如:Class X b=X();

优化

当对象作为函数参数,在对函数进行调用时会调用复制构造函数对形参进行初始化工作,此时会产生额外的开销,我们可以将函数的参数写为对象的引用来避免额外的开销,如果担心对象的值在函数中会被改变,我们也可以用常引用的方式。

对于如下的写法:

Complex fun(Complex c){}

我们可以改写为:

Complex fun(Complex& c){}
Complex fun(const Complex& c) {}

你可能感兴趣的:(c++复制构造函数)