• <C++>解密 构造函数和析构函数


    🛒本文收录于专栏【大战C++】
    📢专栏目的是对于C++的讲解,重点的逐个击破,会持续输出,欢迎免费订阅!!

    🍔彩蛋:
    🔎牛客网是一个兼具 面经、刷题、求职 的神器,是我们的最佳选择,我用过很不错呢~ 👉点击注册开始刷题
    小鸣人因为准备考研,学完C++准备浅练下算法,后续会推出牛客网刷题记录专栏,到时希望大家多多关注~

    ✨发光的不是梦,而是逐梦的自己✨

    在这里插入图片描述


    🔎对象的初始化和清理

    C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置


    1.构造函数和析构函数

    一个对象或者变量没有初始状态,对其使用后果是未知
    同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

    C++用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作,对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供,但编译器提供的构造函数和析构函数是空实现

    • 构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
    • 析构函数主要作用在于对象销毁前系统自动调用,执行一些清理工作
      构造函数语法类名(){}
    1. 构造函数,没有返回值也不写void
    2. 函数名称与类名相同
    3. 构造函数可以有参数,因此可重载
    4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

    析构函数语法~类名(){}

    1. 析构函数,没有返回值也不写void
    2. 函数名称与类名相同,在名称前加上符号 ~
    3. 析构函数不可以有参数,因此不可重载
    4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
    class Person
    {
    public:
    	//构造函数
    	Person()
    	{
    		cout << "Person的构造函数调用" << endl;
    	}
    	//析构函数
    	~Person()
    	{
    		cout << "Person的析构函数调用" << endl;
    	}
    
    };
    
    void test01()
    {
    	Person p;
    }
    
    int main() {
    	
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    2.构造函数的分类及调用

    两种分类方式:

    1. 按参数分为: 有参构造和无参构造
    2. 按类型分为: 普通构造和拷贝构造

    三种调用方式:

    1. 括号法
    2. 显示法
    3. 隐式转换法

    代码举例:

    //1、构造函数分类
    // 按照参数分类分为 有参和无参构造   无参又称为默认构造函数
    // 按照类型分类分为 普通构造和拷贝构造
    
    class Person {
    public:
    	//无参(默认)构造函数
    	Person() {
    		cout << "无参构造函数!" << endl;
    	}
    	//有参构造函数
    	Person(int a) {
    		age = a;
    		cout << "有参构造函数!" << endl;
    	}
    	//拷贝构造函数
    	Person(const Person& p) {
    		age = p.age;
    		cout << "拷贝构造函数!" << endl;
    	}
    	//析构函数
    	~Person() {
    		cout << "析构函数!" << endl;
    	}
    public:
    	int age;
    };
    
    //2、构造函数的调用
    //调用无参构造函数
    void test01() {
    	Person p; //调用无参构造函数
    }
    
    //调用有参的构造函数
    void test02() {
    
    	//2.1  括号法,常用
    	Person p1(10);
    	//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
    	//Person p2();
    
    	//2.2 显式法
    	Person p2 = Person(10); 
    	Person p3 = Person(p2);
    	//Person(10)单独写就是匿名对象  当前行结束之后,马上析构
    
    	//2.3 隐式转换法
    	Person p4 = 10; // Person p4 = Person(10); 
    	Person p5 = p4; // Person p5 = Person(p4); 
    
    	//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
    	//Person p5(p4);
    }
    
    int main() {
    
    	test01();
    	//test02();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    🔥2.1拷贝构造函数调用时机

    C++中拷贝构造函数调用时机通常有三种情况

    1. 使用一个已经创建完毕的对象来初始化一个新对象
    2. 值传递的方式给函数参数传值
    3. 以值方式返回局部对象
    class Person {
    public:
    	Person() {
    		cout << "无参构造函数!" << endl;
    		mAge = 0;
    	}
    	Person(int age) {
    		cout << "有参构造函数!" << endl;
    		mAge = age;
    	}
    	Person(const Person& p) {
    		cout << "拷贝构造函数!" << endl;
    		mAge = p.mAge;
    	}
    	//析构函数在释放内存之前调用
    	~Person() {
    		cout << "析构函数!" << endl;
    	}
    public:
    	int mAge;
    };
    
    //1. 使用一个已经创建完毕的对象来初始化一个新对象
    void test01() {
    
    	Person man(100); //p对象已经创建完毕
    	Person newman(man); //调用拷贝构造函数
    	Person newman2 = man; //拷贝构造
    
    	//Person newman3;
    	//newman3 = man; //不是调用拷贝构造函数,赋值操作
    }
    
    //2. 值传递的方式给函数参数传值
    //相当于Person p1 = p;
    void doWork(Person p1) {}
    void test02() {
    	Person p; //无参构造函数
    	doWork(p);
    }
    
    //3. 以值方式返回局部对象
    Person doWork2()
    {
    	Person p1;
    	cout << (int *)&p1 << endl;
    	return p1;
    }
    
    void test03()
    {
    	Person p = doWork2();
    	cout << (int *)&p << endl;
    }
    
    
    int main() {
    
    	//test01();
    	//test02();
    	test03();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    🔥2.2构造函数调用规则

    默认情况下,c++编译器至少给一个类添加3个函数
    1.默认构造函数(无参,函数体为空)
    2.默认析构函数(无参,函数体为空)
    3.默认拷贝构造函数,对属性进行值拷贝

    构造函数调用规则如下:

    • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
    • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
    class Person {
    public:
    	//无参(默认)构造函数
    	Person() {
    		cout << "无参构造函数!" << endl;
    	}
    	//有参构造函数
    	Person(int a) {
    		age = a;
    		cout << "有参构造函数!" << endl;
    	}
    	//拷贝构造函数
    	Person(const Person& p) {
    		age = p.age;
    		cout << "拷贝构造函数!" << endl;
    	}
    	//析构函数
    	~Person() {
    		cout << "析构函数!" << endl;
    	}
    public:
    	int age;
    };
    
    void test01()
    {
    	Person p1(18);
    	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
    	Person p2(p1);
    
    	cout << "p2的年龄为: " << p2.age << endl;
    }
    
    void test02()
    {
    	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
    	Person p1; //此时如果用户自己没有提供默认构造,会出错
    	Person p2(10); //用户提供的有参
    	Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供
    
    	//如果用户提供拷贝构造,编译器不会提供其他构造函数
    	Person p4; //此时如果用户自己没有提供默认构造,会出错
    	Person p5(10); //此时如果用户自己没有提供有参,会出错
    	Person p6(p5); //用户自己提供拷贝构造
    }
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    🔥2.3深拷贝与浅拷贝

    深浅拷贝是面试经典问题

    浅拷贝简单的赋值拷贝操作
    深拷贝在堆区重新申请空间,进行拷贝操作

    class Person {
    public:
    	//无参(默认)构造函数
    	Person() {
    		cout << "无参构造函数!" << endl;
    	}
    	//有参构造函数
    	Person(int age ,int height) {
    		
    		cout << "有参构造函数!" << endl;
    
    		m_age = age;
    		m_height = new int(height);
    		
    	}
    	//拷贝构造函数  
    	Person(const Person& p) {
    		cout << "拷贝构造函数!" << endl;
    		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
    		m_age = p.m_age;
    		m_height = new int(*p.m_height);
    		
    	}
    
    	//析构函数
    	~Person() {
    		cout << "析构函数!" << endl;
    		if (m_height != NULL)
    		{
    			delete m_height;
    		}
    	}
    public:
    	int m_age;
    	int* m_height;
    };
    
    void test01()
    {
    	Person p1(18, 180);
    
    	Person p2(p1);
    
    	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;
    
    	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
    }
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    第一次看深拷贝与浅拷贝的面试题有点怀疑人生~

  • 相关阅读:
    WPF性能优化:形状(Shape)、几何图形(Geometry)和图画(Drawing)的使用
    MySQL(5)
    使用Java实现汉诺塔问题~
    双指针扫描、滑动窗口
    FTX和Binance摩擦升级?一文梳理“吵架”背后始末
    小笔记:03-jquery-对前端option的一些操作
    QT mysql 数据库线程池 与数据库操作封装
    java使用 TCP 的 Socket API 实现客户端服务器通信
    Three.JS程序化建模入门
    Typora+MinIO+Python代码打造舒适协作环境
  • 原文地址:https://blog.csdn.net/m0_64996150/article/details/125726308