• C++学习笔记-构造函数和析构函数


    构造函数和析构函数是C++的重要组成部分,了解构造函数和析构函数有助于深入了解C++

    构造函数

    构造函数产生的原因

    在C++中,有时候需要在对象创建的时候初始化数据,如果采用普通函数的话,每次初始化都要调用函数,显得麻烦,另外,如果忘记初始化,那么其结果是未知的。
    在C++中使用构造函数来完成初始化,使得对象在生成的时候就完成初始化动作

    构造函数定义

    1. C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数
    2. 构造函数在定义时可以有参数
    3. 没有任何返回类型的声明
    class Test
    {
    public:
    	//构造函数 无参构造函数 默认构造函数
    	Test()
    	{
    		a = 10;
    	}
    	
    	//带参数的构造函数
    	Test(int mya)
    	{
    		a = mya;
    	}
    	
    	//赋值构造函数 copy构造函数
    	Test(const Test & obj)
    	{
    		;
    	}
    protected:
    private:
    	int a;
    };
    

    构造函数的调用

    1. 自动调用:一般情况下C++编译器会自动调用构造函数
    2. 手动调用:在一些情况下则需要手工调用构造函数
    //无参构造函数调用,适合C++默认的构造函数
    Test t0;
    
    //有参构造函数掉用:3种
    Test t1(10); //自动调用
    Test t2 = 11; //自动调用
    Test t3 = Test(12); //手动调用
    
    //copy构造函数调用:4种
    Test t4;
    //第一种:
    Test t5 = t4;//定义并初始化,用t4初始化t5-->t5 = t4不同,后者属于浅拷贝
    //第二种
    Test t6(t4);
    //第三种
    //如果存在函数void func(Test p){},那么在类做函数参数的时候会调用copy构造函数
    Test t7(10);
    func(t7);
    //第四种
    //在定义返回值为类的函数时,会调用copy构造函数,此时生成一个匿名对象
    Test func()
    {
        Test t8(10);
        return t8;
    }
    //这个函数引出匿名对象的生命周期问题
    Test t9;
    t9 = func();//这种方法会有三次构造三次析构,属于浅拷贝
    Test t9 = func();//这种方法直接将匿名对象转化为t9,只经历两次构造和两次析构
    

    特殊的构造函数

    1. 无参构造函数
      当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
    2. 拷贝构造函数
      当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制

    构造函数的规则和理解

    • 当类中没有定义任何一个构造函数时,c++编译器会提供无参构造函数和拷贝构造函数
    • 当类中定义了任意的非拷贝构造函数,c++编译器不会提供无参构造函数
    • 当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
    • 默认拷贝构造函数成员变量简单赋值
    • 构造函数是C++中用于初始化对象状态的特殊函数
    • 构造函数在对象创建时自动被调用
    • 构造函数和普通成员函数都遵循重载规则
    • 拷贝构造函数是对象正确初始化的重要保证
    • 必要的时候,必须手工编写拷贝构造函数

    构造函数的初始化列表

    产生的原因

    由于在多个类中存在包含关系,导致在包含其他类的类中初始化不明确
    此时会造成没有合适的默认构造函数
    如果有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错

    使用

    Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
    {
        // some other assignment operation
    }
    

    初始化:被初始化的对象正在创建
    赋值:被赋值的对象已经存在
    成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
    初始化列表先于构造函数的函数体执行

    #include "iostream"
    using namespace std;
    
    class ABC
    {
    public:
    	ABC(int a, int b, int c)
    	{
    		this->a = a;
    		this->b = b;
    		this->c = c;
    		printf("a:%d,b:%d,c:%d 
    ", a, b, c);
    		printf("ABC construct ..
    ");
    	}
    	~ABC()
    	{
    		printf("a:%d,b:%d,c:%d 
    ", a, b, c);
    		printf("~ABC() ..
    ");
    	}
    protected:
    private:
    	int a;
    	int b;
    	int c;
    };
    
    class MyD
    {
    public:
    	MyD():abc1(1,2,3),abc2(4,5,6),m(100)
    	{
    		cout<<"MyD()"<<endl;
    	}
    	~MyD()
    	{
    		cout<<"~MyD()"<<endl;
    	}
    
    protected:
    private:
    	ABC abc1;
    	ABC abc2;
    	const int m;
    };
    
    int run()
    {
    	MyD myD;
    	return 0;
    }
    
    int main()
    {
    	run();
    	system("pause");
    	return 0;
    }
    
    #include "iostream"
    using namespace std;
    
    class ABCD 
    {
    public:
    	ABCD(int a, int b, int c)
    	{
    		this->a = a;
    		this->b = b;
    		this->c = c;
    		printf("ABCD() construct, a:%d,b:%d,c:%d  
    ", this->a, this->b, this->c);
    	}
    	~ABCD()
    	{
    		printf("~ABCD() construct,a:%d,b:%d,c:%d  
    ", this->a, this->b, this->c);
    	}
    	int getA() 
    	{
    		return this->a;
    	}
    protected:
    private:
    	int a;
    	int b;
    	int c;
    };
    
    
    class MyE
    {
    public:
    	MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
    	{
    		cout<<"MyD()"<<endl;
    	}
    	~MyE()
    	{
    		cout<<"~MyD()"<<endl;
    	}
    	MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
    	{
    		printf("MyD(const MyD & obj)
    ");
    	}
    
    protected:
    	//private:
    public:
    	ABCD abcd1;
    	ABCD abcd2;
    	const int m;
    
    };
    
    int doThing(MyE mye1)
    {
    	printf("doThing() mye1.abc1.a:%d 
    ", mye1.abcd1.getA()); 
    	return 0;
    }
    
    int run()
    {
    	MyE myE;
    	doThing(myE);
    	return 0;
    }
    
    int main()
    {
    	run();
    	system("pause");
    	return 0;
    }
    

    析构函数

    析构函数产生的原因

    构造函数在对象被创建时候调用,而析构函数则相反,是在对象被销毁时候调用,用于清理内存或者变量
    C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数

    析构函数定义

    ~ClassName()
    
    1. 析构函数没有参数也没有任何返回类型的声明
    2. 析构函数在对象销毁时自动被调用
    3. 析构函数调用机制:C++编译器自动调用

    总结

    当类中有成员变量是其它类的对象时
    首先调用成员变量的构造函数
    调用顺序与声明顺序相同
    之后调用自身类的构造函数
    析构函数的调用秩序与对应的构造函数调用秩序相反

    class Test
    {
    public:
    	Test()
    	{
    		cout<<"构造函数自动被调用"<<endl;
    	}
    
    	~Test()
    	{
    		cout<<"析构函数自动被调用"<<endl;
    	}
    protected:
    private:
    };
    ···
    
  • 相关阅读:
    从尾到头打印链表-Python
    上台阶三种实现(斐波那契数列)-Python
    Windows/Linux安装python2.7,pycharm和pandas——《利用Python进行数据分析》
    部署(Django )
    第一个Django模型
    第一个Django Project(创建一个简单的博客)
    几个算法基础
    回文序列—Palindrome
    学习笔记:CentOS7学习之二十:shell脚本的基础
    学习笔记:CentOS7学习之十七: Linux计划任务与日志的管理
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664739.html
Copyright © 2020-2023  润新知