C++构造函数和析构函数

本文详细介绍了C++中构造函数和析构函数的作用,以及它们如何自动处理对象的初始化和清理工作。通过实例展示了构造函数的调用方式,包括无参构造、有参构造和拷贝构造。同时,讨论了深拷贝与浅拷贝的概念,以及在涉及堆内存管理时如何避免浅拷贝带来的问题。文章强调了在涉及内存分配时正确实现构造函数和析构函数的重要性,以确保程序的健壮性和安全性。
摘要由CSDN通过智能技术生成

对象的初始化和清理是两个非常重要的安全问题,一个对象或者变量没有初始状态,对其使用后果是未知,同样的使用完一个对象或变量,没有及时清理,也会造成—定的安全问题。
C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。


构造函数:主腰作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
 

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供的构造函数和析构函数是空实现。

构造函数和析构函数的语法

 构造函数:

1、没有返回值,也不用也void;

2、函数名与类名一致;

3、构造函数可以有参数,也可以重载;

4、程序在调用对象时候会自动调用构造函数,无须手动调用,而且只会调用一次。

 析构函数:

1、没有返回值,也不用也void;

2、函数名与类名一致,但需要在函数名前加上波浪号:~;

3、析构函数不能带参数,所以不能发生函数重载;

4、程序在对象销毁前时候会自动调用析构函数,无须手动调用,而且只会调用一次。

构造函数的分类

按参数分为:有参构造和无参构造

按类型分为:普通构造和拷贝构造

class A
{
 public:
    A(){}//无参构造函数

    A(int a){}//有参构造函数

    A(const A &p)//拷贝构造函数,将对象p的属性传入该构造函数,在其实现过程中可以使用p的属性
    {
        a=p.a;
        b=p.b;
    }
    

 private:
    int a;
    int b

};

 构造函数的调用

1、括号法

如下图所示,其调用比较简单,也是最常用的方法:

class A
{
 public:
    A()//无参构造函数
    {
        cout<<"无参构造函数调用"<<endl;
    }

    A(int x,int y)//有参构造函数
    {
        a=x;
        b=y;
    }

   A(const A &p)//拷贝构造函数
    {
        a=p.a;
        b=p.b;
    }

 private:
    int a;
    int b

};

int main()
{
    括号法:
    A p1;//调用A()无参构造函数
    A p2(10,20);//调用A(int x,int y)有参构造函数
    A p3(p2);调用A(const A &p)拷贝构造函数,即p3这个对象将p2的所有属性都拷贝了一份
}

         注意

无参构造的调用不能带括号,不能A p1(),这样写编译器会认为是一个函数声明。

2、显示法

A p1;//无参构造还是老样子

A p2 = A(10,20);//调用有参构造函数

A p3 = A(p2);//调用拷贝构造函数

         注意

        上图中等号右侧的A(10,20)和A(p2)属于匿名对象,其特点是当前执行结束后,会被系统会立即回收掉。

3、隐式转换法

A p1;//无参构造还是老样子

A p2 = {10,20};//调用有参构造函数,这是多参数情况,如果有参构造只有一个参数,就不用大括号了。

A p3 = p2;//调用拷贝构造函数

构造函数的调用规则

  1. 如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造;
  2. 如果用户定义拷贝构造函数,C++不会再提供其他构造函数。

深拷贝与浅拷贝

  1. 浅拷贝:简单的赋值拷贝操作,浅拷贝带来的问题就是堆区的内存重夏释放
  2. 深拷贝:在堆区重新申请空间,进行拷贝操

 在调用拷贝构造函数的时候,即会有2个不同构造函数,如果部分内存开辟到了堆区,然后需要手动释放该部分内存,但是这2个不同的构造函数都要释放相应的堆区,第一个释放了,那么第二个怎么办呢?那就报错了,这就是浅拷贝带来的问题。这就需要深拷贝来解决。

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "无参构造函数的调用" << endl;
	}

	A(int age,string name)
	{
		m_Age = age;
		m_Name = name;
		cout << "有参构造函数的调用" << endl;
	}

	A(const A &p)
	{
		m_Age = p.m_Age;
		m_Name = p.m_Name;
		cout << "拷贝构造函数的调用" << endl;
	}

	~A()
	{
		cout << "析构函数的调用" << endl;
	}

private:
	int m_Age;
	string m_Name;
};

int main()
{
	A p1;//调用无参构造函数
	A p2 = p1;//调用拷贝构造函数
}

 上面是简单的构造函数调用,没有开辟堆区的内存,编译器正常执行:

 接下来我们要在堆区申请内存空间,如下:

 对有参构造、拷贝构造和析构函数进行了修改:

class A
{
public:
	A()
	{
		cout << "无参构造函数的调用" << endl;
	}

	A(int age,string name)
	{
		m_Age = age;
		//m_Name = name;
		m_Name = new string(name);
		cout << "有参构造函数的调用" << endl;
	}

	A(const A &p)
	{
		m_Age = p.m_Age;
		m_Name = p.m_Name;
		cout << "拷贝构造函数的调用" << endl;
	}

	~A()
	{
		if (m_Name != NULL)
		{
			delete m_Name;
			m_Name = NULL;
		}
		cout << "析构函数的调用" << endl;
	}

private:
	int m_Age;
	string *m_Name;
};

int main()
{
	A p1(18, "小明");//调用无参构造函数
	A p2 = p1;//调用拷贝构造函数
}

出现如下问题:

 即有参构造和拷贝构造这两个函数中的m_Name变量都开辟到同一内存空间,释放变量的时候先先执行拷贝构造函数的析构代码,即把m_Name变量释放了;然后需要执行有参构造的析构函数的析构代码,编译器就访问不到m_Name变量的地址了,即使访问到了,也是非法访问,所以报错了。

解决方法-----------使用深拷贝

	A(const A &p)
	{
		m_Age = p.m_Age;
		//m_Name = p.m_Name;浅拷贝
		m_Name = new string(*p.m_Name);//深拷贝
		cout << "拷贝构造函数的调用" << endl;
	}

在拷贝构造函数里面我们对传入的对象重新开辟新的空间,那么就有2个内存空间了,2个函数分别指向2个不同的空间,这样就解决了上面的问题了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值