构造函数,拷贝函数和析构函数的区别

构造函数,拷贝函数和析构函数的区别

默认函数

因为c++主要是为了面向对象,所以具有三大基本特征:封装,继承,多态,而c++的类中六个默认函数便主要是为了封装的过程,即:构造函数,拷贝函数,析构函数,赋值操作数的重载,取地址操作符的重载,const修饰的取地址操作符重载
这里主要讲构造函数,拷贝函数,析构函数的区别

构造函数

构造函数的函数名称必须与类名同名,并且构造函数没有返回值,构造函数的主要功能是为了初始化函数

class A {
public:
	A(int a,int b,char c) {
		_a = a;
		_b = b;
		_c = c;
	}

	int _a;
	int _b;
	char _c;
};

int main() {
	A i(1,2,'s');
	cout << i._a << i._b << i._c << endl;
	return 0;
}

我们观察上面代码,可以看到我们定义了一个类类型A,在主函数创建了对象i,并把1,2,s的数据传输给了_a,_b,_c成员变量,而类中A函数便是构造函数。我们要注意是,类中_a,_b,_c变量仅仅只是声明对象,并没有开辟空间创建对象,他们是在构造函数A中初始化对象的
那如果我们不创建构造函数,能否成功呢?
答案是可以,因为编译器会自带默认构造函数,默认构造函数即编译器自动初始化函数

class A {
public:
	int _a=1;
	int _b=2;
	char _c='s';
	int main() {
	A i;
	cout << i._a << i._b << i._c << endl;
	return 0;
};

这里便是直接进行默认构造函数的初始化,上面代码的输出同样也是12s,不过我们可以用更方便的缺省构造函数,这个同样也是默认构造函数

class A {
public:
	A(int a = 1, int b = 2, char c = 's') {//缺省构造函数
		_a = a;
		_b = b;
		_c = c;
	}

	int _a;
	int _b;
	char _c;
};

int main() {
	A i1(3,4,'t');
	A i2;
	cout << i1._a << i1._b << i1._c << endl;
	cout << i2._a << i2._b << i2._c << endl;
	return 0;
}

上面代码输出是34t和12s,进行传参时则使用传参值,没有传参则默认初始化

⭐值得注意的是,在我们没有写构造函数时,编译器默认构造函数会有一个偏心处理:
1、内置类型不进行初始化 2、而自定义类型则会调用他的构造函数初始化,若自定义类型没有构造函数,则同样也不会进行初始化

析构函数

析构函数没有返回值,没有参数,析构名在类名前面加一个~,若类未定义,则编译器会自动生成默认的析构函数,在对象生命周期结束时,会自动调用析构函数
析构函数的主要作用是为了释放如malloc在堆上面开辟内存空间的对象,进行清理

class Stack {
public:
	Stack(int capacity = 4) {
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr) {
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = _capacity = 0;
	}
	~Stack() {
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	int* _a;
	int _top;
	int _capacity;
};
int main() {
	Stack i;
	return 0;
}

拷贝函数

没有返回值,以类名作为函数名

class A {
public:
	A(int a = 1, int b = 2, char c = 's') {
		_a = a;
		_b = b;
		_c = c;
	}

	A(const A& d) {//拷贝函数
		_a = d._a;
		_b = d._b;
		_c = d._c;
	}

	int _a;
	int _b;
	char _c;
};

int main() {
	A i;
	A i2(i);//A i2=i;同样调用拷贝函数
	cout << i2._a << i2._b << i2._c << endl;
	return 0;
}

经过调试后,我们可以发现A i2(i);这里的代码调用了拷贝函数
⭐值得注意的是,这里拷贝函数的传参传的是引用,意味着d是类A的i的引用,若没有引用,那么会导致我拷贝函数里定义d也要拷贝函数,这会形成一个传值无穷递归的问题,所以需要加一个&,来进行引用
如果引用传参,不是做输出型参数,最好用const &来做保护(即内部成员变量不会改变)
注:在对于函数内的类的传值传参,还有返回值都是要调用拷贝构造的
对于编译器自动生成的拷贝函数,不会去区分内置类型和自定义类型,都会处理
1、内置类型,字节序的浅拷贝
2、自定义类型,会去调用他的拷贝构造
但由于是浅拷贝,对于自己开辟内存单元的(如上面的Stack)则难以实现默认拷贝构造,会生成崩溃

int main(){
	Stack st1;
	Stack st2(st1);
	return 0;
}

上面代码就会崩溃,因为st1和st2在堆中生成空间,先创建st1,再创建st2,在进行析构函数时,先对st2的_a进行释放空间,轮到st1时候,因为_a的空间已经释放过,则无妨二次释放,所以到导致程序崩溃

总结

函数功能
构造函数初始化对象
拷贝函数对对象进行拷贝
析构函数释放堆上开辟的空间

以上便是构造函数,拷贝函数和析构函数的区别了

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值