C++ —— 拷贝构造函数
- 引言
- 编译器给的拷贝构造函数
- 默认拷贝构造函数
- 调用拷贝构造函数的情况
引言
用四种方式创建对象的示例代码如下:
#include <iostream>
using namespace std;class Car
{
public:string c_brand;float c_acceleration;Car() {c_brand.clear(); c_acceleration = 0.0; cout << "普通 Car constructor" << endl;}~Car() {cout << "普通 Car destructor" << endl;}void show() {cout << "品牌: " << c_brand << ", 加速度: " << c_acceleration << endl;}};int main() {Car c1;Car c2 = Car();Car* c3 = new Car;Car* c4 = new Car();delete c3;delete c4;return 0;
}
以上程序运行时会调用四次构造函数和析构函数。
编译器给的拷贝构造函数
用一个已存在
的对象创建新的
对象,不会调用(普通)构造函数,而是调用拷贝构造函数
。如果类中没有定义
拷贝构造函数,编译器
将提供一个拷贝构造函数
,它的功能是把已存在
对象的成员变量赋值给
新对象的成员变量。
用一个已存在
的对象创建新的
对象的语法:
类名 新对象名 (已存在的对象名);
类名 新对象名 = 已存在的对象名;
示例代码如下:
int main() {Car c1;c1.c_brand = "Tesla"; c1.c_acceleration = 100;Car c2 (c1); // 用已存在的对象c1创建新对象c2# Car c2 = c1; // 效果是一样的c2.show();return 0;
}
运行效果如下:
普通 Car constructor
品牌: Tesla, 加速度: 100
普通 Car destructor
普通 Car destructor
第一行日志是创建c1
的时候显示出来的,创建c2
时,没有日志显示,说明没有调用普通构造函数
。最后,c1
和c2
销毁的时候,都调用了析构函数。
对于目前的Car
类,没有定义拷贝构造函数
,但是编译器给了一个拷贝构造函数
,它的功能是把c1
的成员变量赋值给c2
的成员变量。
默认拷贝构造函数
拷贝构造函数
的语法:
类名 (const 类名& 对象名) {......}
对现有Car
类增加拷贝构造函数
,代码如下:
#include <iostream>
using namespace std;class Car
{
public:string c_brand;float c_acceleration;Car() {c_brand.clear();c_acceleration = 0.0;cout << "普通 Car constructor" << endl;}// 默认拷贝构造函数Car(const Car &cc) {c_brand = "拷贝的" + cc.c_brand;c_acceleration = 100000 + cc.c_acceleration;cout << "拷贝 Car constructor" << endl;}~Car() {cout << "普通 Car destructor" << endl;}void show() {cout << "品牌: " << c_brand << ", 加速度: " << c_acceleration << endl;}
};int main() {Car c1;c1.c_brand = "Tesla"; c1.c_acceleration = 100;Car c2 (c1);c2.show();return 0;
}
运行结果如下:
普通 Car constructor
拷贝 Car constructor
品牌: 拷贝的Tesla, 加速度: 100100
普通 Car destructor
普通 Car destructor
拷贝构造函数的注意事项:
- 访问权限必须是
public
; - 函数名必须与类名
相同
; - 没有返回值,不写
void
; - 如果类中
定义了
拷贝构造函数,编译器将不提供
默认的拷贝构造函数; 拷贝构造函数
可以重载
,可以有默认参数
;但是,重载的时候,形参中一定要有类本身
的常引用
;
带参数
的拷贝构造函数
示例代码如下:
Car(const Car &cc, int n) {c_brand = "带参数的拷贝构造函数" + cc.c_brand;c_acceleration = cc.c_acceleration * n;cout << "带参数的拷贝构造函数 Car constructor" << endl;
}
调用带参数
的拷贝构造函数
的main()
函数示例代码如下:
int main() {Car c1;c1.c_brand = "Benz";c1.c_acceleration = 111;Car c2(c1, 9);c2.show();return 0;
}
运行效果如下:
普通 Car constructor
带参数的拷贝构造函数 Car constructor
品牌: 带参数的拷贝构造函数Benz, 加速度: 999
普通 Car destructor
普通 Car destructor
- 如果类中
重载
了拷贝构造函数
却没有
定义默认的拷贝构造函数
,编译器
也会提供
默认的拷贝构造函数。
调用拷贝构造函数的情况
- 用
已经存在的对象
创建新对象
的时候,会调用拷贝构造函数; - 以
值传递
的方式调用函数
时,如果实参
为对象
,会调用拷贝构造函数
;
void func(Car c) {c.show();
}int main() {Car c1;c1.c_brand = "BMW"; c1.c_acceleration = 4;func(c1);return 0;
}
Car
类的定义不变,增加函数void func(Car c) {...}
,并在main
函数中调用。运行结果如下:
普通 Car constructor
拷贝 Car constructor
品牌: 拷贝的BMW, 加速度: 100004
普通 Car destructor
普通 Car destructor
- 函数以
值
的方式返回对象
时,可能
会调用拷贝构造函数
(VS会调用,Linux
不会,g++编译器
做了优化
)。代码如下:
Car func() {Car c;c.c_brand = "Ferrari";c.c_acceleration = 66;cout << "func c地址:" << &c << endl;return c;
}int main() {Car c1 = func();c1.show();cout << "main c1地址:" << &c1 << endl;return 0;
}
运行结果如下:
普通 Car constructor
func c地址:0x7ffe469b4c50
品牌: Ferrari, 加速度: 66
main c1地址:0x7ffe469b4c50
普通 Car destructor
可以看到,此时并没有调用拷贝构造函数(我使用的是Linux
系统)。这是因为,编译器
认为没必要创建对象,可以不销毁func()
函数中创建的那个对象c
。
感谢浏览,一起学习!