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
。
补充内容
C++ —— 拷贝构造函数(补充)
感谢浏览,一起学习!