1. 程式人生 > >類的六大預設建構函式

類的六大預設建構函式

預設的建構函式和解構函式,等於放棄了自己初始化和清除的機會;預設的拷貝構造和預設的賦值函式,採用“位拷貝和值拷貝”。若類中出現指標時,這兩個函數出錯。

class String
{
public:
String(const char *str = NULL);//構造
~String();//析構
String (const String &s);//拷貝構造
String& operator=(const String &s);//賦值
String* operator&();//普通引用方法
String* operator&()const;//常引用方法
private
: char* m_str; };

建構函式與解構函式

建構函式和解構函式表明了一個物件的由生到死的過程。一個物件只能呼叫一次構造方法,一個類有且只有一個析構方法。

建構函式作用:
1、例項化物件 2、對物件成員進行初始化 3、強制型別轉化(通過中間橋樑實現)

#

類的資料成員的初始化方式(二者效率不同)
1、初始化列表方式
2、函式體內賦值
3、類的const 常量只能在初始化列表內初始化,不能在函式體內進行賦值。

#

構造順序
1、遇到物件自動呼叫其構造方法,如若有繼承關係時,則先呼叫父類的構造方法;當主函式中構造物件完成時自動呼叫解構函式。
2、先構造者後析構,因為建構函式中,物件是通過堆疊的方式進行儲存的,同理 析構時按照出棧的順序。

成員物件初始化的次序完全不受他們在初始化列表中的次序,只與成員物件在類中的宣告次序有關。

#include<iostream>
using namespace std;

class Test
{
public:
    Test()
    {
    cout<<this<<endl;//6c
    }
    Test(int d,int num):number(num),data(d)
    {
        cout<<this<<endl;//64
        cout<<"data-->"
<<this->data<<endl; cout<<"number-->"<<this->number<<endl; } ~Test() { cout<<"~Test()"<<this<<endl; } private: int data; int number; }; void main() { Test t; Test t2(1,2); }

這裡寫圖片描述

強制型別轉化

#include<iostream>
using namespace std;
class Test
{
public:
    Test()
    {
        cout<<"Test()"<<this<<endl;
    }
    Test(int d):data(d)
    {
        cout<<"Test()"<<this->data<<endl;
    }
    /*int GetData()const
    {
        return data;
    }
    */
    operator int()
    {
        return data;
    }

    ~Test()
    {
        cout<<"Free Test()"<<this<<endl;
    }
private:
    int data;
};
void main()
{
    //Test t1(); 宣告函式,能編譯、能執行,但是是錯誤的
    Test t(1);
     t =100;//100通過構建函式構造中間零時物件,物件給物件賦值
     //若建構函式前面加上explicit關鍵字時,則賦值時必須顯示呼叫即  t =(Test) 100;    
     int value;
    //通過公有方法獲取data值
    //value = t.GetData();
    //呼叫operator int()方法,將物件型別強制轉換為值型別
    value = t;

}

這裡寫圖片描述

建構函式,有兩種方式:

1、定義無參建構函式 2、定義所用帶預設值的建構函式 且 二者方式只能用一種。一個類只能有一個帶預設值的建構函式。不然程式產生二義性。當帶預設值的引數,沒有初始化時,則產生隨機值。

#include<iostream>
using namespace std;

class Test
{
public:
    //無參構造
    Test()
    {}
    Test(int d):data(d)
    {
        cout<<data<<endl;
    }
    //帶預設值的構造
    /*
    Test(int d=0)
    {
        data = d;
        cout<<data<<endl;
    }
    */
private:
    int data;
};
void main()
{
    Test t(2);
}

拷貝構造與賦值函式

拷貝建構函式:用一個已有物件去初始化一個新的物件。

產生拷貝構造的條件:

1、用已有物件去初始化新的物件
2、函式引數型別傳遞
3、函式返回值型別的物件。

include<iostream>
using namespace std;
include<vld.h>
class Test
{
public:
    Test()
    {}
    Test(int d):data(d)
    {}
    void print()
    {
        cout<<data<<endl;
    }
    //不用引用,自己解釋自己會產生迴圈遞迴
    Test(const Test &other)
    {
        data = other.data;
    }
    ~Test()
    {
        cout<<"~Test()"<<endl;
    }
private:
    int data;
};
void fun(Test s1)
{
    cout<<"fun()"<<endl;
}
Test fn()
{
    Test s2(1);
    //返回s2之前會產生無名的臨時物件,即用s2去構造物件 因此呼叫拷貝建構函式
    return s2;
}
void main()
{
    Test t(2);
    //兩者一樣
    //Test t1(t);
    Test t1= t;
    t.print();
    t1.print();
    //物件作為函式引數
    fun(t1);
    //物件作為函式的返回值
    t=fn();
}

這裡寫圖片描述

return 返回時,實際呼叫了拷貝建構函式構造了一個無名的臨時物件

int  Sum(int a,int b)
{
    return sum=a+b;
    //返回的不是sum,sum在離開作用域時,已經被釋放;會產生一個臨時無名物件,並將sum 值傳給該物件。
}
void main()
{
    int val=Sum(1,2);
}

賦值

賦值相當於a物件的成員引數,給b的成員引數賦值。
1、是否把返回值的型別宣告為該型別的引用。
2、是否把傳入的引數宣告為常量引用。
3、是否釋放自己已有空間。
4、是否判斷傳入的引數和當前的例項(*this)是不是一個例項。

#

#include<iostream>
using namespace std;
#include<vld.h>
class Test
{
public:
    Test(int d=0):data(d)
    {}
    void print()
    {
        cout<<data<<endl;
    }
    Test(const Test &other)
    {
        data = other.data;
    }
    /*
    1.const:表明物件的資料成員在賦值過程中,不被修改
    2.&s:減少呼叫一次拷貝建構函式,也提高效率,在程式中,當物件不受函式作用域的影響時,可以使用&
    3.Test 為了是連續物件間賦值t1=t2=t3;
    4.在返回類的物件*this時,會產生臨時物件呼叫拷貝建構函式。&為了不構造臨時物件,減少呼叫拷貝構造
    t1.operator=(t2.operator=(t3))
    */
    Test& operator=( const Test &s)
    {
        if(this != &s)
        {
            data = s.data;
        }
        //返回當前物件
        return *this;
    }
    ~Test()
    {
        cout<<"~Test()"<<endl;
    }
private:
    int data;
};

void main()
{
    Test t(10);
    Test t1(t);
    Test t2;
    t2 = t1;
    //t2.operator=(t1)
    //t2.operator=(&t2,t1)
    //下面的const用來說明當前物件不允許修改
    //Test& operator(Test *const this,const  Test &s )
}

普通引用方法與常引用方法

#include<iostream>
using namespace std;
#include<vld.h>

class String
{
public:
    String(char *str=NULL)
    {
    //構造的物件為NULL時,String a(""|NULL|0)和String a()不一樣
        if(str == NULL)
        {
            m_str = new char[1];
            *m_str = '\0';
        }
        else
        {   
            m_str = new char[strlen(str)+1];
            strcpy(m_str,str);
        }
    }
    //深拷貝
    String(const String &s)
    {
         m_str = new char[strlen(s.m_str)+1];
        strcpy(m_str,s.m_str);
    }
    //深賦值
    String& operator=(const String &other)
    {
        if(this != &other)
        {
        String tmp=other;
        char *pstr=m_str;
        m_str= tmp.m_str;
        tmp.m_str = pstr;
        }
        return *this;

    }
    //普通引用方法
    String* operator&()
    {
        return this;
    }
    //常方法
    const String* operator&()const
    {
        return this;
    }
    ~String()
    {
        delete []m_str;
        m_str = NULL;
    }
private:
    char *m_str;
};
void main()
{
    String s("123");
    String s1=s;
    String s2;
    s2=s1;
    String *p =&s1;
    const String s3("Hello");
    const String *q =&s3;
}