C++中的构造函数、析构函数以及拷贝构造函数

只要创建了一个类编译器会默认创建三个构造,
1.无参构造
2.有参构造
3.拷贝构造
这三个函数你不写编译器就自动用编译器默认的,你写了就用你的。

// 构造函数和析构函数
// 类实例化一个对象时强制调用构造函数,对象销毁后自动调用析构函数进行清理工作。
// 如果没有手动写出构造和析构函数,编译器会自动补上,只不过是空实现。
#include <iostream>
using namespace std;


class fun {

public: // 如果私有化 则该类将不能在外部实例化对象
	fun() { // 没有返回值也不写void
		// 构造函数一般进行初始化的工作。
		// 可以有形参,可以重载,有参数的是有参构造,该构造为无参构造
		// 创建对象时会自动调用构造且只会调用一次。
		cout << "构造函数被调用!" << endl;
	}

	fun(int a) {
		cout << "有参构造 " << a << endl;
	}

	// 析构函数 语法  " ~类名(){} "
	~fun() {// 没有返回值也不写void
		// 析构函数进行清理工作。
		// 析构函数没有形参,不能发生重载。
		// 析构函数在对象死亡的时候被自动调用且只调用一次。
		cout << "析构函数被调用!" << endl;
	}
};

class Person {
	int age;
	
public:
	// 普通构造函数
	Person(){
	
	}
	Person(int a) {
	
	}
	// 拷贝构造函数 
	// 不写拷贝构造函数时,系统默认提供,拷贝p的所有属性到新的对象中
	Person(const Person& p) {
		// 拷贝时不能修改本体所以要写const
		age = p.age;
	}
};

// 调用
void test(){
	Person p1;  // 默认构造的调用
	// 1.括号法
	Person p2(10); // 调用有参构造函数
	Person p3(p2); // 括号法调用拷贝构造函数

	// 注意:1、调用默认构造时 不要加括号。
	// Person pp(); // 编译器认为这是函数的声明,而不是创建对象。
	
	
	
	// 2.显示法
	Person p4 = Person(10); // 调用有参构造
	Person p5 = Person(p4); // 显示法调用拷贝构造

	Person(10); // 创建匿名对象,特点:当前行运行结束后会立即调用它的析构函数结束对象的生命

	// Person(p3);
	// 注意:2、不要利用匿名实例化对象调用拷贝构造。
	// 编译器会认为这是个对象的声明 Person(p3) === Person p3;
	// 编译器就会报语法错误,p3重定义


	// 3.隐式转换法
	Person p6 = 10; // 相当于 Person p6 = Person(10);
	Person p7 = p6; // 隐式法 调用 拷贝构造

}

class ccopy {
public:
	int age;
public:
	ccopy() {

	}
	ccopy(int age) {
		this->age = age;
	}
	ccopy(const ccopy & c) {
		this->age = c.age + 1;  // 这样写测试更明显
	}

	~ccopy() {
	}

};

void dowork(ccopy p) {
	// 这里实参传入时会触发拷贝构造函数
	// 我们将拷贝构造函数修改为  新对象时旧对象的age+1;
	cout << "拷贝打印 " << p.age << endl; // 这里打印2 因为拷贝构造中 cc1.age+1 == 2

}
ccopy dowork2() {
	ccopy c(1);
	return c; // 这里函数结束后栈被释放,
			  //但是释放前拷贝了c对象,所以返回 通过触发拷贝构造 产生的对象
}
void text01() {
	// 拷贝函数的调用时机
	// 1.使用一个已经初始化完毕的对象来初始化一个新对象。
	ccopy c1(10);
	ccopy c2 = c1;

	// 2.值传递的方式给函数参数传值
	ccopy cc1(1);
	dowork(cc1); // 调用形参为ccopy对象的函数 传入的cc1 age==1;

	// 3.值方式返回局部对象
	ccopy cc2 = dowork2(); // 这里cc2.age == 2; 
						// 因为调用函数dowork2 时触发拷贝
					 // dowork2中实例化了c对象然后栈销毁前触发拷贝构造c.age+1 == 2
	cout << "cc2 == " << cc2.age << endl; // cc2 == 2
}


int main()
{
	text01();
	//fun f;

	
	return 0;
}

如果你写了拷贝构造函数,编译器将不会提供其他的构造函数

class demo {

public:
	demo(const demo& d) { // 只写拷贝构造
	
	}
};

void t_demo() {
	demo d; // 这里实例化不了demo对象。因为没有构造函数
}

深拷贝与浅拷贝

所谓浅拷贝就是拷贝的时候考虑的少,不管被拷贝的对象属性是什么全拿过来赋给新的对象,而深拷贝则是对被拷贝对象的属性值多考虑了一步。当然考虑不周就会付出代价,下面列举出浅拷贝的问题以及深拷贝的优化。

// 深拷贝与浅拷贝
#include <iostream>
using namespace std;

// 浅拷贝:逐字节拷贝
// 编译器默认提供的就是浅拷贝
// 浅拷贝会出现的问题
// 1.堆区的内存重复释放
class student {
public:
	student(int age) {
		this->age = age;
	}

	~student() { // 析构函数
		delete(height); // 释放对象通过new开辟的堆区空间
					// 当触发浅拷贝时将原来对象的地址拷贝了一份,
					// 但是原来的对象以及释放了该地址空间
					// 拷贝来的这个对象再次释放这个地址就会出现段错误
	}

	// 深拷贝 解决上述问题
	student(const student& s1) {
		this->age = s1.age;
		this->height = new int(*s1.height);// 像这种重新申请空间拷贝有效值称为深拷贝
	}

	int age;
	int* height;
};


int main()
{
	student s1(10);
	s1.height = new int(10);

	student s2 = s1;  // 触发拷贝构造

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值