C++面向对象(十):拷贝构造函数

C++面向对象(十):拷贝构造函数

拷贝构造函数

  • 拷贝构造函数是构造函数的一种
  • 当利用已存在的对象创建一个新对象时(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化
  • 拷贝构造函数的格式是固定,接收一个 const 引用作为参数
#include <iostream>
using namespace std;
class Car {
	int m_price;
	int m_length;
public:
	// 构造函数
	Car(int price = 0, int length = 0) :m_price(price), m_length(length){
		cout << "Car(int price = " << m_price << " int length =" << m_length << endl;
	}
	// 拷贝构造函数
	// 默认情况下,没必要写这个函数
	Car (const Car &car) :m_price(car.m_price), m_length(car.m_length) {
		cout << "Car(cosnt Car &car)" << endl;
	}
	void display () {
		cout << "price=" << m_price << ", length=" << m_length << endl;
	}
};
int main() {
	Car car1(666, 6);
	// 利用已经存在的car3对象创建了一个car4对象
	// car4初始化时会调用拷贝构造函数
	Car car2(car1);
	// 没有拷贝函数时,类似于这种操作:
	/*car4.m_price = car3.m_price;
	car4.m_length= car3.m_length;*/
	car2.display();
	getchar();
	return 0;
}

何时调用

class Car {
	int m_price;
	int m_length;
public:
	// 构造函数
	Car(int price = 0, int length = 0) :m_price(price), m_length(length){
		cout << "Car(int price = " << m_price << " int length =" << m_length << ")\n";
	}
    // 拷贝构造函数
	Car (const Car &car) :m_price(car.m_price), m_length(car.m_length) {
		cout << "Car(cosnt Car &car)" << endl;
	}
	void display () {
		cout << "price=" << m_price << ", length=" << m_length << endl;
	}
};
int main () {
	// 创建了四个对象
	Car car1 (6, 666); // 非拷贝构造
	Car car2 (car1); // 拷贝构造
	Car car3 = car2; // 与上面的等价,拷贝构造
	Car car4;  // 非拷贝构造
	car4 = car3; // 是一个赋值操作(默认是浅复制),并不会调用拷贝构造函数
	// 打印为 6, 666
	car4.display ();
	getchar ();
	return 0;
}

调用父类的拷贝构造函数

#include <iostream>
using namespace std;
class Person {
public:
	int m_age;
	Person(int age = 0) :m_age(age) {}
	Person(const Person &person) :m_age(person.m_age) {}
};
class Student : public Person {
public:
	int m_score;
	Student(int age = 0, int score = 0) :Person(age), m_score(score) {}
	// 如果不调用父类的拷贝函数成员,就不会进行拷贝: Person(student)
	Student (const Student &student) : Person(student), m_score(student.m_score) {}
};
int main () {
	Student stu1 (6, 666);
	// 默认就会拷贝,旧对象的值会将新对象中的所有数据覆盖掉
	Student stu2 (stu1);
	// 如果不调用父类的拷贝函数成员,就不会进行拷贝
	cout << stu2.m_age << endl;
	cout << stu2.m_score << endl;
	getchar ();
	return 0;
}

浅拷贝和深拷贝

浅拷贝

编译器默认的提供的拷贝是浅拷贝(shallow copy)

  • 将一个对象中所有成员变量的值拷贝到另一个对象
  • 如果某个成员变量是个指针,只会拷贝指针中存储的地址值,并不会拷贝指针指向的内存空间
  • 可能会导致堆空间多次free的问题
#include <iostream>
using namespace std;
class Car {
	int m_price;
	char *m_name;
public:
	Car (int price = 0, char *name = NULL) :m_price(price), m_name(name) {
	}
	void display () {
		// 危险,m_name后面的值可能被回收,之后就变成了垃圾数据
		cout << "price=" << m_price << ", length=" << m_name<< endl;
	}
};
Car *g_car;
void test () {
	char name[] = { 'B', 'M', 'W', '\0', };
	// 这样堆空间的指针(name)指向栈空间的数据(BMW),很危险
    // 因为不能控制栈空间的生命周期可能会变成野指针
	g_car = new Car (100, name);
}
int main () {
	test ();
	g_car->display ();
	getchar ();
	return 0;
}
深拷贝

为了避免这种情况,而在对空间申请同样大小的空间,并且将栈空间的数据拷贝过来就行了:

class Car {	
	int m_price;
	char *m_name;
	// 定义一个拷贝函数
	void copyName (const char *name) {
		if (name == NULL) return;
		// 申请新的对空间,需要额外的1存储\0,加上{}将对堆空间的数据初始化为0
		// 以保证最后一个一定为0
		m_name = new char[strlen (name) + 1]{};
		// 拷贝字符串数据到新的堆空间,直到遇上\0为止
		// 会报错,我执意要用这个,所以右键项目->属性->C/C++->命令行
		// 加上-D"_CRT_SECURE_NO_WARNINGS"
		strcpy (m_name, name);
	}
public:
	// 重写构造函数
	Car (int price = 0, const char *name = NULL) :m_price (price) {
		copyName (name);
	}
    // 重写拷贝构造函数
    Car (const Car &car) :m_price(car.m_price) {
		copyName (car.m_name);
	}
	// double free: 如果有两个对象,就会delete两次
	~Car () {
		if (m_name == NULL) return;
		delete[] m_name;
		m_name = NULL;
	}
	void display () {
		cout << "price=" << m_price << ", length=" << m_name << endl;
	}
};
  • 如果需要实现深拷贝(deep copy),就需要自定义拷贝构造函数
  • 将指针类型的成员变量所指向的内存空间,拷贝到新的内存空间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值