C++构造函数及析构

661 阅读5分钟

一、构造函数和析构函数

概念:对象的初始化和清理是两个非常重要的安全问题,一个对象或变量没有初始化时,对其使用后果是未知的。同样使用完一个变量,没有及时清理,也会造成一定的安全问题。C++为了f给我提供这种问题解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用,完成初始化和对象清理的工作。 对象初始化和清理工作是编译器强制我们要做的事情,即便你不提供初始化和清理工作,编译器也会给你增加默认操作,只是这个默认化操作不会做任何事情,所以编写类就应该提供初始化函数。

 #include<iostream>
 using namespace std;
 
 class MyClass
 {
 public
 	//1-构造函数写法
//没有返回值 也不写void 函数名与类名相同 
//可以有参数,可以发生重载 
//由编译器自动调用,不需要手动调用,且编译器只会调用一次

 MyClass()
 {
	cout << "MyClass的构造函数调用" << endl;
	}

//2-析构函数写法
//没有返回值 不写void 函数名与类名相同 在函数名称面前加~
//不可以有参数,不可以发生重载
//由编译器自动调用,不要手动调用,编译器也只调用一次

~MyClass()
{
	cout << "~MyClass的析构函数调用" << endl;
}
     
 };
 
 void test01()
 {
 MyClass myclass; //实列化对象
     
 }
 int main()
 {
 test01();
 return 0;
 }

二、构造函数分类及调用

1.分类

按照参数进行分类 有参构造函数 无参构造函数(默认构造函数)

按照类型进行分类 普通构造函数 拷贝构造函数

 class PurSon
 {
 public:
 
 	PurSon() //默认构造
 	{
 		cout << "PerSon的默认构造函数调用" << endl;
  	}
  	
 	PurSon(int age)//有参构造
 	{
 		m_Age = age;
 		cout << "PerSon的有参构造函数调用" << endl;
  	}
  
 	PurSon(const PurSon &p)//拷贝构造
 	{
 		m_Age = p.m_Age;
 		cout << "PurSon拷贝函数调用" << endl;
 	}
 	
    	~PurSon()//析构
 	{
	cout << "PurSon析构函数调用" << endl;
 	}
 	int m_Age;
  };

拷贝:类名(const 类名 &n)

必须加上const 防止拷贝内容被不经意的修改

必须加上引用 & ,如果不加 & 参数为值传递将拷贝新的临时变量引起无限递归 2.调用

 void test01()
 {
 	//无参构造函数
  	PurSon P;
  	
 	//有参构造函数
 	//1)括号法
 	PurSon p2(10);//有参构造调用
 	PurSon p3(p2);//拷贝构造函数调用
 	cout << "P3的年龄:" << p3.m_Age << endl;
 	
 	//2)显示法
 	PurSon p4 = PurSon(10);//有参构造函数
 	PurSon p5 = PurSon(p4);//拷贝构造哈数
 	
 	//3)隐式法
 	PurSon p6 = 10; //等价于 PurSon p6=PurSon(10);可读性不强 不建议使用
 	PurSon p7 = p6;
 }

注意点:

PurSon p();//不可以用括号法,调用无参构造函数 原因PurSon p(),编译器认为是一个函数声明
PurSon(10);//匿名函数对象 特点:当前执行完后,系统就立即回收
PurSon(p5);//不可以调用拷贝构造函数来初始化匿名对象PurSon(p5)编译器认为代码为 PurSon p5;

三、拷贝构造函数的调用时机

   #include<iostream>
  using namespace std;
  
  class PerSon
 {
 public:
 	PerSon() 
 	{
	cout << "PerSon的默认构造函数调用" << endl;
 	}
 	PerSon(int age)
 	{
	m_Age = age;
	cout << "PerSon的有参构造函数调用" << endl;
 	}
 	
 	PerSon(const PerSon & p)
 	
 	{
	m_Age = p.m_Age;
	cout << "PerSon拷贝函数调用" << endl;
  	}
  	~PerSon()
 	{
	cout << "PerSon析构函数调用" << endl;
 	}
 	int m_Age;
 };
 //1.使用一个已经创建好的对象来初始化另一个对象
  void test03()
 {
  	PerSon p1(18);
  	PerSon p2(p1);
  	cout << "p2年龄为: " << p2.m_Age << endl;
  }
 //2.以值传递的方式 给函数的参数传值
 void dowork(PerSon p)
 {
 
 }
  void test04()
 {
 	PerSon p1;
 	dowork(p1);
 }
 //3.以值的方式返回局部变量
 PerSon dowork2()
 {
 	PerSon p1;
 	return p1;
 }
 void test05()
 {
  	PerSon p = dowork2();
   }
 int main()
 {
 	test03();
  	return 0;
 }

四、构造函数调用规则

1.默认情况下:系统会默认给我们写的类添加至少3个函数

1)默认构造函数(无参,函数体为空)

2)默认析构函数(无参,函数体为空)

3)默认拷贝构造函数对类中非静态成员属性简单值拷贝

2.如果用户定义了 有参构造函数,那么系统就不会提供默认构造函数了,但是依然会提供拷贝构造函数

3.如果用户定义了 拷贝构造函数,那么系统就不会提供其他的普通构造函数了

五、深拷贝和浅拷贝

如果类中有属性开辟到堆区,那么释放时候,由于浅拷贝问题导致堆区内容会被重复释放,程序down掉。 利用深拷贝来解决浅拷贝带来的问题。

    #include<iostream>
    using namespace std;
 class PtrSon
  {
   public:
   	PtrSon(char * name, int age) //普通构造函数
  	{
  	cout << "Person的有参构造函数调用" << endl;
	m_Name = (char *)malloc(strlen(name) + 1);//开辟空间
	strcpy(m_Name, name);
	m_Age = age;
 	}
 	
 	PtrSon(const PtrSon & p) //定义拷贝构造
 	{
	m_Age = p.m_Age;
	//m_Name=p.m_Name  系统提供的浅拷贝 
	//利用深拷贝(用户自己定义的拷贝构造函数) 解决浅拷贝带来的问题
	m_Name=(char *)malloc(strlen(p.m_Name) + 1);给拷贝的函数开辟新空间
	strcpy(m_Name, p.m_Name);
 	}
 	
  	~PtrSon() //提供析构函数
 	{
	if (m_Name = NULL)
	{
		cout << "PtrSon的析构函数调用" << endl;
		free(m_Name); //释放堆区所开辟的空间 如果用户没有提供深拷贝 则调用系统提供的默认浅拷贝
		              //会导致析构函数释放同一块堆区(p1、p2指向同一块内存)内存两次
		m_Name = NULL;
	}
 	}
 	char * m_Name;  //char *堆区 
  	int m_Age;
  };
 void test06()
 {
 	PtrSon p1("Tom", 18);
 	cout << "p1的姓名:" << p1.m_Name << " 年龄:" << p1.m_Age << endl;
 	
 	PtrSon p2(p1);
 	cout << "p2的姓名:" << p2.m_Name << " 年龄:" << p2.m_Age << endl;
 }
 int main()
 {
 	test06();
 	return 0;
 }