C++之构造函数

本文详细介绍了C++中的构造函数,包括无参构造、带默认值构造、有参构造和拷贝构造。重点讨论了拷贝构造函数,特别是浅拷贝和深拷贝的概念,并通过代码示例展示了深拷贝的实现,以防止对象间的内存冲突。此外,还探讨了赋值运算符重载和析构函数在对象生命周期中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是构造函数?
创建一个对象之后,就需要初始化该对象,构造函数就是用来初始化该对象的。
2.构造函数有什么作用?
构造函数的作用是初始化对象的数据成员。类对象被创建的时候,编译系统为对象分配内存空间,并自动调用该构造函数,由构造函数完成初始化工作。
3.构造函数的分类有哪些?

  • 无参构造函数
  • 待默认值的构造函数
  • 有参构造函数
  • 拷贝构造函数
    • 一种特殊的构造函数,当对象之间复制时会自动调用拷贝构造函数
    • 若类中没有定义拷贝构造函数,系统会自动生成一个拷贝构造函数
    • 拷贝构造函数具有单个形参,该形参(常用const修饰)是对该类型的引用,当定义一个新对象并对他进行初始化时,将显示调用拷贝构造函数,当该类型的对象传递给函数返回该类型的对象时,将隐式调用拷贝构造函数
    • 当类中有一个数据成员是指针时,或者有成员表示在构造函数中分配的其它资源,必须显示调用拷贝构造函数。

4.浅拷贝和深拷贝的区别?
对于下边的拷贝构造函数,如果在拷贝name时,直接写为:

_name=src._name

那么在析构时,新对象和旧对象就会析构同一块内存空间,程序就会奔溃;
而深拷贝就是重新为新对象开辟一段内存空间,不只是简单地赋值
。(就好比你的作业写完了,你的同桌拿去看了,但是你的同桌不能交你的作业,因为你还要交,而一份作业不能交两次,所以你的同桌要交作业的话,就必须照着你的作业抄一份,然后你们各交各的)

下边我们通过一个代码来详细了解构造函数:

#include<iostream>
using namespace std;
class Person
{
public:
	int _age;
	int _sex;
	char* _name;//成员属性
	Person()//默认构造函数
	{
		_name = NULL;
		cout << "Person()" << endl;
	}

	Person(int age)//带一个参数的构造函数
	{
		cout << "Person(int age)" << endl;
		_age = age;
		_name = NULL;
	}
	Person(int age, int sex,const char* name)//带多个参数的构造函数
	{
		cout << "Person(int age, int sex,  char* name)" << endl;
		_age = age;
		_sex = sex;
		//_name = name;//浅拷贝 指针给指针赋值
		//深拷贝
		_name = new char[strlen(name) + 1];
		memset(_name, 0, strlen(name) + 1);
		for (int i = 0; i < strlen(name); i++)
		{
			_name[i] = name[i];
		}
	}
	Person(const Person& src)//拷贝构造函数
	{
		cout << "Person( Person src)" << endl;
		_age = src._age;
		_sex = src._sex;
		//预防浅拷贝
		if (NULL != src._name)
		{
			_name = new char[strlen(src._name) + 1];
			memset(_name, 0, strlen(src._name) + 1);
			for (int i = 0; i < strlen(src._name); i++)
			{
				_name[i] = src._name[i];
			}
		}
		else
		{
			_name = NULL;
		}
	}
	Person& operator=(const Person& src)//等号运算符重载
	{
		cout << "void operator=(const Person& src)" << endl;
		//防止自赋值 
		if (this == &src)
		{
			return *this;
		}
		//防止内存泄漏
		delete[]_name;
		//防止浅拷贝
		if (NULL != src._name)
		{
			_name = new char[strlen(src._name) + 1];
			memset(_name, 0, strlen(src._name) + 1);
			for (int i = 0; i < strlen(src._name); i++)
			{
				_name[i] = src._name[i];
			}
		}
		else
		{
			_name = NULL;
		}
	}

	
	~Person()//析构函数
	{
		if (NULL != _name)
		{
			cout << _name << endl;
		}
		cout << "~Person()" << endl;
		if (NULL != _name)
		{
			delete[]_name;
		}
		_name = NULL;
	}
	void work(/* Person *this */)//每一个成员方法在传参的时候都会默认传一个this指针
	{
		//this = NULL;
		cout <<_name<<":work" << endl;//默认加上this的解引用 this->_name
		eat();//this->_eat;
	}
	void eat()//普通成员函数
	{
		cout << _name<<"eat" << endl;
	}
	void show()
	{
		if (NULL != _name)
		{
			cout << "name:" << _name << endl;
		}
		cout << "age:" << _age << endl;
		cout << "sex" << _sex << endl;
	}
};
//
Person fun(Person& p)
{
	int a = 10;
	Person tmp(10);
	return tmp;//返回一个对象 不可以返回引用,函数调用结束之后,底层指针就会变为野指针
}
int main()
{
    //测试模块一:
	Person p1;
	//p1._name = "zhangsan";//c++中不可以把常量字符串赋值给非常量指针,这句会报错
	char name[] = { "zhangsan" };
	p1._name = name;
	p1.eat();//默认取哪个对象,就取哪个对象的地址
	
	//测试模块二:
	char name[] = { "zhangsan" };
	Person p1(32, 1, name);
	p1.show();

	Person p2;
	Person p3();//对象名之后不可加(),否则系统按照函数声明处理掉了

    //测试模块三:
    char name[] = { "zhangsan" };
	Person p1(32, 1, name);
	Person p2(p1);//拷贝构造
	p1.show();
	p2.show();
	
    //测试模块四
	Person p3(15,1,"lisi");
	p3 = p2 = p1;
	p3.show();
     
    //测试模块五:
	Person p1(31, 1, "zhangsan");//三个函数都能构造
    Person* p2 = new Person();//没有参数构造
    delete p2; 
    
    //测试模块六
	Person p3;//没有参数构造
	Person p4 = 20;//一个参数构造
	//使用20生成临时对象
	//使用临时对象拷贝p4
	//析构临时对象
	//如果出现上述步骤顺序,会被直接优化成构造p4
	Person p5 = p3;//拷贝构造
	Person p6(p3);//拷贝构造
    
    //测试模块七
    Person p3;
	p3 = fun(p3);
	Person p4 = fun(p3);
	return 0;
}

测试模块一:调用无参构造函数
在这里插入图片描述
测试模块二:调用有参构造函数,先构造后析构
在这里插入图片描述
测试模块三:先调用普通构造对成员变量进行初始化,再调用拷贝构造函数构造一个新对象p2,同时对p2的成员变量进行初始化。
在这里插入图片描述
测试模块四:赋值运算符的“=”是属于后边对象的,因为p2的name为空,所以p3的name也被置空
在这里插入图片描述
如果为“p2=p3",则执行结果就会出现name
在这里插入图片描述
测试模块五:
在这里插入图片描述
测试模块六:拷贝构造函数是用一个已经存在的对象去构造一个新的对象,而等号赋值运算符的两个对象都是已经存在的
在这里插入图片描述
特别注意:这里有一个优化构造概念
在这里插入图片描述
测试模块七:

  • 先构造p3;
  • 再构造临时临时对象tmp;
  • 用tmp拷贝构造一个返回值对象;(return tmp)
  • 函数执行完成,析构临时变量tmp;
  • 因为p3已经存在,所以“p3 = fun(p3)"调用赋值运算符重载函数,用返回值对象给p3赋值;
  • 析构返回值对象;
  • 再构造临时对象tmp;
  • 再构造返回值对象;
  • 再用返回值对象构造p4;(以上三步符合优化构造的条件,所以这里直接为构造p4) ;
  • 析构临时对象;
  • 析构返回值对象;(相当于析构p4) 析构p3
    解释:为什么这里返回值对象相当于p4呢?
    答:编译器有自己的优化策略,如果发现用构造返回值对象去构造新对象时,就会直接省略构造临时返回值对象这一步骤,直接去构造新对象,也就是p4,即上边说的优化构造

在这里插入图片描述
构造函数部分参考:https://blog.csdn.net/qq_29339467/article/details/90719951.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值