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

(一)构造函数

  要点

  • 定义:构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。

  • 声明语法:

    派生类::派生类名(参数表):基类名1(基类1初始化参数列表),...,基类名n(基类n初始化参数列表),成员对象名1(成员对象1初始化参数表),...,成员对象名m(成员对象m初始化参数表),基本类型成员初始化

    {

       派生类构造函数的其他初始化操作;

    }
    构造函数的初始化参数列表: 构造函数名(参数1,参数2,...):成员1(参数1),成员2(参数2)....{}

  • 构造函数名字和类名相同

  • 构造函数没有返回值

  • 不写构造函数,每一个类中都存在默认的构造函数,默认的构造函数是没有参数

    • default显示使用默认的构造函数

    • delete 删掉默认函数

    • 当我们自己写了构造函数,默认的构造函数就不存在

  • 构造函数不需要自己调用,在构造对象的时候自己调用

    • 构造函数决定的了对象的长相

    • 无参构造函数可以构造无参对象

    • 有参构造函数,对象必须要带有参数

  • 构造函数允许被重载和缺省

  • 构造函数一般情况是公有属性

  • 构造函数一般是用来给数据成员初始化

  • 构造函数允许调用另个构造函数,必须采用初始化参数列表的写法

  •  派生类构造函数执行的一般顺序如下:

    • (1)调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)。 

    • (2)对派生类新增的成员初始化,初始化顺序按照它们在类中声明的顺序 

    • (3)执行派生类的构造函数体中的内容。

  • 构造函数初始化列表中基类名、对象名之间的顺序无关紧要,它们各自出现的序可以是任意的,无论它们的顺序怎样安排,基类构造函数的调用和各个成员对象的初性物顺序都是确定的。

  • 具体代码:

    ​
    #include <iostream>
    #include <string.h>
    using namespace std;
    class MM {
    public:   
    	//构造函数
    	//MM() = default;          //使用的函数是默认的构造函数    
         MM(){      cout << "无参构造函数" << endl;    }
         MM(int a){ cout << "具有一个参数的构造函数" << endl; }
    protected:
    };
    
    class Girl {
     public:
     Girl() = delete; //删除默认的构造函数
    protected:
     };
    class Student {
    public:  
      Student() = default; 
      Student(string name, int age){    //做初始化操作       
    	  m_name = name;        m_age = age;  
    	  }  
      void printStudent(){        cout << m_name << "\t" << m_age << endl;    }
     protected:  
      string m_name;  
      int m_age;
     };
     //初始化参数列表
    class Test {
    public:   
    	//构造函数特殊写法  
      Test(int a, int b) :a(a), b(b)  {    } 
       Test():Test(0,0) {}       
    	//无参构造函数调用有参构造函数  
       //构造委托 
       void print(){      cout << a << "\t" << b << endl;    }
    protected:  
    	int a=0; 
    	int b=0;
    };
    struct Data {  
    	int a;  
    	int b;    
    	int c;    
    	Data(int a) :a(a){}Data(int a, int b, int c) :a(a), b(b), c(c){ cout << "调用三个参数的构造函数" << endl; } 
    	void print(){        cout << a << "\t" << b << "\t" << c << endl;    }
    };
    void testData() { Data data = { 1,2,3 }; } //这个过程也是调用构造函数过程,{}中数据个数要和构造函数参数一致
    void printData(Data data){	data.print();
    }
    void printData2(Data& data) { data.print(); }
    
    int main(){
    #if 0
    	 MM mm;
    	 MM girl(1);
    	 //Girl girl;
    	 //普通对象
    	 Student stu("npc", 18);
    	 stu.printStudent();
    	 //new一个对象
    	 Student* pstu = new  Student("执灯", 29);
    	 pstu->printStudent();
    
    	 //对象数组
    	 Student stuArray[3];			//无参构造函数构造
    	 Test test;
    	 test.print();
    
    #endif
    	 Data data(1, 2, 3);
    	 printData(data);
    	 printData2(data);
    	 return 0;
     }
    
    ​

                   

(二) 复制构造函数

拷贝构造函数也叫做复制构造函数。

拷贝构造函数特性

  • 不写拷贝构造函数,存在一个默认拷贝构造函数

  • 拷贝构造函数名和构造函数一样,算是构造函数特殊形态

  • 拷贝构造函数的唯一的一个参数就是对对象引用

    • 普通引用

    • const引用

    • 右值引用--->移动拷贝

  • 当我们通过一个对象产生另一个对象时候就会调用拷贝构造函数

  • 具体代码:

    #include <iostream>
    #include <string>
    using namespace std;
    class MM
    {
    public:
    	MM() {}
    	MM(MM& object)
    	{
    		cout << "调用复制构造函数" << endl;
    	}
    protected:
    
    };
    
    class Girl
    {
    public:
    	Girl(string name, int age) :name(name), age(age) {}
    	Girl() :Girl("", 0) {}
    	Girl(const Girl& object)
    	{
    		//构造函数就是通过一个对象赋值另一个对象
    		name = object.name;
    		age = object.age;
    		cout << "调用拷贝构造函数" << endl;
    	}
    	void print()
    	{
    		cout << name << "\t" << age << endl;
    	}
    protected:
    	string name;
    	int age;
    };
    void printGirl(Girl girl)   //Girl girl=实参
    {
    	girl.print();
    }
    void printMM(Girl& girl)
    {
    	girl.print();
    }
    
    void testGirl()
    {
    	Girl girl("张三", 18);
    	Girl mm(girl);
    	mm.print();
    	Girl beauty = mm;
    	beauty.print();
    	cout << "传入普通变量" << endl;
    	printGirl(girl);
    	cout << "传入引用" << endl;
    	printMM(girl);
    	//匿名对象的拷贝构造函数
    	//匿名对象是右值,右值引用
    	Girl test = Girl("匿名", 18);
    }
    
    class Boy
    {
    public:
    	Boy(string name, int age) :name(name), age(age) {}
    	Boy(Boy&& object)		//右值引用
    	{
    		name = object.name;
    		age = object.age;
    		cout << "右值引用的拷贝构造" << endl;
    	}
    	Boy(Boy& object)
    	{
    		name = object.name;
    		age = object.age;
    		cout << "普通拷贝构造" << endl;
    	}
    protected:
    	string name;
    	int age;
    };
    void testBoy()
    {
    	cout << ".............." << endl;
    	Boy boy("boy", 18);
    	Boy gg = boy;						//调用普通的对象
    	Boy coolman = Boy("sdfd", 28);		//右值引用的拷贝构造函数
    	//没有打印结果,编译器做了优化,看不到
    }
    
    int main()
    {
    	MM mm;
    	MM girl = mm;				//会调用拷贝构造函数
    	MM beauty(girl);			//会调用拷贝构造函数
    	//误区
    	MM  npc;
    	npc = girl;					//运算符重载,不会调用拷贝构造函数
    	testGirl();
    	testBoy();
    	return 0;
    }

(三)析构函数

析构函数特性

  • 声明语法:函数名等于 ~和类名

  • 作用:在该类对象消亡之前进行一些必要的清理工作。

  • 析构函数没有参数,所以析构函数不能被重载也不能缺省

  • 对象死亡(作用域结束、生命周期结束)的最后一个事情是调用析构函数

  • 析构函数都是公有属性的

  • 什么时候写析构函数?

    • 当类的数据成员new了内存就需要自己手动写析构函数

  • 不写析构函数,也会存在一个析构函数,但是不具有释放new的内存功能

  • 具体代码:

    #include <iostream>
    using namespace std;
    class MM
    {
    public:
    	MM()
    	{
    		p = new int;
    	}
    	//手动调用析构函数
    	void freeMemory()
    	{
    		delete p;
    		p = nullptr;
    	}
    	~MM()
    	{
    		cout << "我是析构函数" << endl;
    		delete p;
    		p = nullptr;
    	}
    protected:
    	int* p;
    
    };
    int main()
    {
    	 //版本1:
    	{
    		MM mm;
    		MM* p = new MM;		 	
    		delete p;				//括号作用范围完,立刻马上调用析构函数 
    		p->freeMemory();相当于delete p;
    	}
    		cout << "......" << endl;
    	 //版本2
    		 
    			MM mm;		
    			MM* p = new MM;			
    			delete p;				//立刻马山调用析构函数		 
    	   cout << "......" << endl;
    	return 0;
    }

  • 运行效果: 

  (四)构造函数与析构函数的顺序问题

构造和析构顺序问题

  • 一般情况构造顺序和析构顺序是相反的

  • new对象,调用delete直接被释放

  • static对象,最后释放

具体代码:

#include <iostream>
#include <string>
using namespace std;
class MM {
public:
    MM(string info = "A") :info(info) { cout << info; }
    ~MM() { cout << info; }
protected:
    string info;
};
void testOrder() {
    MM mm1("B");  //构造从上往下 :B, 
    static MM mm2("C");     //构造:C       //最后释放 C   
    MM* p = new MM("D");    //构造:D   
    delete p;               //delete直接调用析构函数:D  D生命周期结束 故打印D,
    MM arr[3];              //构造:AAA             //析构下往上,静态最后一个:AAABC
}
    int main() {
        testOrder();//BCDDAAAAAABC
        return 0;
    }

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尘 关

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值