构造函数,析构函数和拷贝构造函数的初识

本文详细介绍了C++中的构造函数、析构函数和拷贝构造函数。构造函数用于对象初始化,无返回值且名称与类名相同;析构函数在对象销毁时自动调用,负责资源清理;拷贝构造函数用于对象复制,接收一个本类对象的引用作为参数。文章强调了自定义构造函数和析构函数在管理资源时的重要性,并指出在特定情况下可以依赖编译器生成的默认函数。
摘要由CSDN通过智能技术生成

目录

一.构造函数

1.1特性

二.析构函数

2.1概念

2.2特性

三.拷贝构造函数(浅拷贝)

3.1概念

3.2特征


一.构造函数

在认识构造函数之前我们先看一段代码,

class Date
{
public:
	void SetDate(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() {
		
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;  //年
	int _month; //月
	int _day; //日
};
int main() {
	Date s1;
	s1.SetDate(2001,1,8);
	s1.Print();

	Date s2;
	s2.SetDate(2000, 2, 8);
	s2.Print();
}

对于Date类来说,可以通过SetDate公有的方法给对象设置内容,,但是每次创建对象的时候都要调用一次未免麻烦,那能否在对象创建时,就将信息设置进去呢?这个时候就要用到构造函数了。

构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次

1.1特性

构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

其特征如下:

1. 函数名与类名相同


2. 无返回值。


3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载。

这时候我们尝试改一下代码

class Date
{
public:
	//函数名与类名相同,无返回值,
	//无参构造函数
	/*Date(){
	}*/

	//二者都可以,都是无参
	//Date() {
	//	_year = 2000;
	//	_month = 1;
	//	_day = 8;
	//}
	// 
	//有参构造函数
	//也就是上一个函数的重载
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() {

		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;  //年
	int _month; //月
	int _day; //日
};
int main() {
	//Date s1;
   //s1.Print();

// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
   //Date s1();这是错误的


	Date s2(2004,5,8);
	s2.Print();
}




 5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

class Date
{
public:
	//自己没有写构造函数,编译器会自动生成一个无参的默认构造函数
    // 如果用户显式定义了构造函数,编译器将不再生成
   //Date(int year, int month, int day) {
   //		_year = year;
//		_month = month;
//		_day = day;
//	}

	void Print() {

		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;  //年
	int _month; //月
	int _day; //日
};
int main() {
// 没有定义构造函数,对象也可以创建成功,因此此处调用的是编译器生成的默认构造函数
	Date s1;
	s1.Print();

}

 6,默认构造函数可以分为三种:无参构造函数全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数,注意:默认构造函数只能有一个。

class Date
{
public:
	//无参
	Date() {
		_year = 2000;
		_month = 1;
		_day = 8;
	}
	//全缺省函数,半缺省函数也是,只不过没有全缺省实用
	Date(int year = 1900, int month = 1, int day = 1) {
		_year = year;
		_month = month;
		_day = day;
	}
	//还要系统默认的
	//三者只能存一
	void Print() {

		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;  //年
	int _month; //月
	int _day; //日
};

但是默认构造函数对内置类型和基本类型:int/char/double/指针...不做处理,对于自定义类型成员变量才会处理:class/struct去定义类型对象。

这也就为啥上面的编译器默认构造函数初始化_year,_month,_day,为随机值的原因。

这时候就会很多人有疑问:在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?s1对象调用了编译器生成的默认构造函数,但是s1对象 year/month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么卵用??

其实不然,先看下面的代码:

class Stack
{
public:
	//默认构造函数
//这里不写默认构造函数,会调用不了,会出错的
	Stack(int capacity = 10)
	{
		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
	
		_top = 0;
		_capacity = capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
 
class MyQueue {
public:
	// 默认生成构造函数就可以用了

	void push(int x) {
	}
	
	int pop() {
	}
	
private:

	Stack _st1;
	Stack _st2;
};

int main() {
	//大致代码意思就是用两个栈实现队列,具体就不写了
	MyQueue q;

}

 总结:一般情况一个C++类,都要自己写构造函数。一般只有少数情况可以让编译器默认生成。

1、类里面成员都是自定义类型成员,并且这些成员都提供了默认构造函数

2、如果还有内置类型成员,声明时给了缺省值

二.析构函数

2.1概念

前面通过构造函数的学习,我们知道一个对象时怎么来的,那一个对象又是怎么没呢的?

析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。

2.2特性

1. 析构函数名是在类名前加上字符~。

2. 无参数无返回值。

3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。


4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

5.内置类型不做处理,自定义类型会调用他的析构函数

//像这种只有内置类型,调用他的析构函数,没有什么意义了,他对内置类型不做处理
class Date
{
public:
	void SetDate(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() {

		cout << _year << "-" << _month << "-" << _day << endl;
	}
	~Date()
	{
		//没有啥用处
		cout << "~Date():" << this << endl;
	}
private:
	int _year;  //年
	int _month; //月
	int _day; //日
};
int main() {
	Date s1;
	s1.SetDate(2001, 1, 8);
	s1.Print();
}


析构函数的用处:

class Stack
{
public:
	//默认构造函数
	Stack(int capacity =10)
	{

		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
	
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		//这种对资源的管理,需要自己定义
		//无返回值
		cout << "~Stack():" << this << endl;
		free(_a);
			_a = nullptr;
			_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
 
class MyQueue {
public:
	// 默认生成构造函数就可以用了
	// 默认生成析构函数就可以用了
	void push(int x) {
	}
	
	int pop() {
	}
	
private:

	Stack _st1;
	Stack _st2;
};

int main() {
	//大致代码意思就是用两个栈实现队列,具体就不写了
	MyQueue q;
}

 

 总结:析构函数跟构造函数有相似之处:类里面成员都是自定义类型成员,并且这些成员都提供了默认析构函数。

三.拷贝构造函数

3.1概念

构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

通俗的来说就是用存在的对象,去初始化另一个对象。

3.2特征
 

拷贝构造函数也是特殊的成员函数,其特征如下:

1. 拷贝构造函数是构造函数的一个重载形式。


2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。

注意:   我们这里讲的是浅拷贝

class Date
{
public:
	//默认构造函数
	Date(int year=1,int month=1,int day=1) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() {

		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//我们不写,编译器会自动调用
	//这里一定要用引用传参
	Date(const Date& s1) {
		cout << "hello" << endl;
		_year = s1._year;
		_month = s1._month;
		_day = s1._day;
	}
private:
	int _year;  //年
	int _month; //月
	int _day; //日
};
int main() {
	Date s1(2004,5,6);
	s1.Print();
	//用s1去初始化s2;
	Date s2(s1);
	s2.Print();
}

 为什么我们非要用引用传参呢?

 

那么在自定类型浅拷贝会做什么呢?

class Stack
{
public:
	//默认构造函数
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		assert(_a);

		_top = 0;
		_capacity = capacity;
	}
	//默认拷贝构造函数
	//自己不写编译器会自动调用
	Stack(const Stack& st) {
		cout << "Stack(const Stack& st)" << endl;
	}

	~Stack()
	{
		//默认析构函数
		//无返回值
		cout << "~Stack():" << this << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
	
private:
	int* _a;
	int _top;
	int _capacity;
};

int main() {
	Stack st;
	Stack st2(st);
}

这段代码是有问题的,会崩溃,不是拷贝构造出现了问题,而是结束的析构函数出现了问题

那么自定类型与内置类型同时存在的情况会做什么?

class Stack
{
public:
	//默认构造函数
	Stack(int capacity =4)
	{

		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
	
		_top = 0;
		_capacity = capacity;
	}
	//默认拷贝构造函数
	//自己不写编译器会自动调用
	Stack(const Stack& st) {
		cout << "Stack(const Stack& st)" << endl;
	}
	~Stack()
	{
		//默认析构函数
		//无返回值
		cout << "~Stack():" << this << endl;
		free(_a);
			_a = nullptr;
			_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
 
class MyQueue {
public:
	// 默认生成构造函数就可以用了
	// 默认生成析构函数就可以用了
	//默认拷贝构造函数就可以用了
	void push(int x) {
	}
	
	int pop() {
	}
private:
	int size = 0;
	Stack _st1;
	Stack _st2;
};

int main() {
	//大致代码意思就是用两个栈实现队列,具体就不写了
	MyQueue q;
	MyQueue mq(q);
}

 

总结:一般的类,自己生成拷贝构造就够用了,只有像栈结构的类,自己管理资源,这样就需要用到之后学到的深拷贝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值