1. 程式人生 > >C++匿名物件與建構函式

C++匿名物件與建構函式

前言: 不得不承認,我是一個內心比較躁動的人。如果沒有一個純粹的學習環境,大部分時間我很難靜下心來,但是我還要說,學習使我感到快樂。對我而言,大部分埋頭啃書的時間都是枯燥乏味的,但是總有那麼一些恍然大悟的瞬間,一些雲開月明的瞬間,讓我興奮不已,那種無比充實而快樂的感覺,真好! 但是,學到的知識就像捧在手裡的雪球,如果不把它攥實或者變大,就會很容易的化掉,最終只剩手心的一點水漬。我曾經無數次想記錄下一些學習上的心得與體會,但是最終都沒有做到。希望這篇文章是一個轉折點。

1,匿名物件 為什麼在這篇文章要把匿名物件和建構函式放一起?如果你覺得他們之間沒有任何關係,那我覺得你一定要看一下下邊的內容。我們先來看一下C++匿名物件的語法。

//這是本文的示例類,下文只談結果,執行效果省略。
class Person {
public:
    Person() {
        cout << "no param constructor!" << endl;
        mAge = 0;
    }
    Person(int age) {
        cout << "1 param constructor!" << endl;
        mAge = age;
    }
    Person(const Person& person) {
        cout
<< "copy constructor!" << endl; mAge = person.mAge; } void PrintPerson() { cout << "Age:" << mAge << endl; } ~Person() { cout << "解構函式已呼叫" << endl; } private: int mAge; };

1.1,匿名物件的構造和析構:

Person p1;          //1,預設構造
Person(); //2,預設構造 Person(100); //3,有參構造 Person(Person(1)); //4,有參構造 Person(Person(p1)); //5,拷貝構造 Person(p1); //6,error,p1重定義 //匿名物件可以呼叫類方法 Person(100).PrintPerson();

從1,2,3行我們很容易發現,匿名函式當代碼執行走完它所在的那一行,就呼叫解構函式釋放掉,這是因為這個物件是匿名了,我們後續的操作中已經無法使用這個物件,所以編譯器(走完 ;這個序列點之後)就自動幫我們釋放掉了。

然而 4,5行是什麼操作?可能好多人都以為 4 是先呼叫有參構造,再呼叫拷貝構造,而 5 行應該是呼叫了兩次拷貝構造,如果你也感覺是這樣,那麼你就非常有必要看一看後邊的內容了。

建構函式的語法相當複雜,我覺得稱之為智障也不為過,雖然這只是開始。我們如果嘗試這寫一些很複雜的巢狀層級很多的程式碼,你會發現這裡會和指標一樣很容易把人繞懵,所以我們只討論有實用意義的語法。

上邊的4,5行疑問暫時保留,這裡先解釋一下6行那裡的錯誤:

易錯點1: b為A的例項化物件,A a = A(b) 和 A(b)的區別? 當A(b)有變數來接的時候,那麼編譯器認為他是一個匿名物件,當沒有變數來接的時候,編譯器認為你A(b) 等價於 A b,所以就造成了重定義錯誤。

2,預設構造和拷貝構造 因為這是兩種相對簡單的建構函式,我們把他們拿到一起來說。

2.1,預設構造

Person P1;           //1,預設構造 (隱式呼叫)
//預設構造不存在 括號法 呼叫
Person P1();         //2,error,編譯器會認為這是一個函式宣告
Person P1 = Person();//3,預設構造(顯式呼叫)

是不是感覺第3行和上邊示例中的4,5行似曾相識,為啥之呼叫了預設構造,應該是 = 右邊呼叫依次無參構造,= 左邊呼叫一次拷貝構造才對啊,事實並不是這樣的。 結合上文,我們不妨做出猜想: 猜想1:

A:匿名物件是建構函式的一個橋樑 B:當匿名物件出現在 = 號右邊時會被左邊的物件接管,這個接管會省去基於 = 號 的拷貝構造。 C:當匿名物件出現在 = 左邊,就會很快被釋放掉。

易錯點2:

2.2,拷貝構造 Person P1() 是一個錯誤的寫法,編譯器會認為這是一個返回值為Person型別,函式名為 P1 的函式宣告。

Person p1;
Person p2(p1);      //1,括號法 (隱式呼叫)最常用
Person p2 = p1;     //2,等號法 (隱式呼叫)隱式轉換
Person p2 = Person(p1); //3,匿名法 (顯式呼叫)

又沒又發現一個簡單的拷貝構造可以寫出來這麼多花樣來,哈哈。還有就是第3行只調用了一次拷貝構造,不是大家想的兩次拷貝構造( = 號左右兩邊各一次)。 這裡是不是已經驗證了我們的猜想?現在我們需要做的就是把所有的形式都分析一下,看一看我們猜想能不能在每種情況下站得住腳。如果能,那我們就記住它!

2.3,顯式呼叫和隱式呼叫 然後我們在這裡講一下顯式呼叫和隱式呼叫。我們可以看到顯式呼叫只有一種,大家都知道建構函式名和類名一樣,匿名法也就是我們說的顯式呼叫法,這裡驗證了匿名物件作為建構函式的一個橋樑。 猜想2: D:顯式呼叫其實就是在類外呼叫了類的建構函式 E:如果等號右邊的匿名物件是一個非物件引數構造,呼叫了有參構造 F:如果右邊的匿名物件是一個物件,呼叫了拷貝構造。

還有就是為什麼隱式呼叫有兩種?一個括號法,一個等號法,其實只有當一個類的建構函式只需要一個傳參的時候(可以又其他有預設值的引數)才會有等號法。等號法的本質,C++語法上,更喜歡稱之為隱式轉換,這個我們後續文章會詳細介紹。

在這裡我要扯一些閒話,如果你是編譯器,你更喜歡顯式呼叫還是隱式呼叫?我覺得大部分人看到顯式呼叫會更容易一點,也就是說隱式呼叫最終會被解析為顯式呼叫。這裡可能是語法層次上的一些優化,隱式呼叫的優勢在於程式碼更緊湊一些,更有利於程式設計師的工作效率。

3.有參構造

Person p1(100);         //(隱式呼叫)最常用
Person p1 = Person(100);//匿名法 (顯式呼叫)
Person p1 = 100;        //等號法 (隱式呼叫)隱式轉換

//相信我,你老闆或者同時看到你寫下邊這種程式碼,一定會削你的!
Person p1(Person(100)); //等價於 Person p1 = Person(100);

這裡再次驗證了我們上邊的猜想,所以我們最後再來熟悉一下上邊的每條猜想: 猜想1:

A:匿名物件是建構函式的一個橋樑 B:當匿名物件出現在等號右邊時會被左邊的物件接管,會省去基於等號的拷貝構造。 C:當匿名物件出現在 = 左邊,就會很快被釋放掉。

猜想2:

D:顯式呼叫其實就是在類外呼叫了類的建構函式 E:如果等號右邊的匿名物件是由一個非物件引數構造,呼叫了有參構造 F:如果右邊的匿名物件是由一個物件構造,呼叫了拷貝構造。