天天看點

Effective C++讀書筆記----自定義類型的傳參和傳回值問題

  • 對于自定義類型,傳參的時候盡可能的使用傳引用來代替傳值。

看如下這個例子:

#include <iostream>
using namespace std;
#include <string>

class Person
{
public:
    Person()//預設的構造函數,如果不給,沒法通過編譯,因為在建立一個派生類的對象是需要調用。
    {
        cout << "Person()" << endl;
    }

    Person(const Person& p)//拷貝構造函數
    {
        cout << "Person(const Person& )" << endl;
    }

    virtual ~Person()//析構函數
    {
        cout << "~Person()" << endl;
    }
private:
    string name;
    string address;
};

class Student: public Person
{
public:
    Student()//預設的構造函數
    {
        cout << "Student()" << endl;
    }
    Student(const Student& s)//拷貝構造函數
    {
        cout << "Student(const Student& )" << endl;
    }
    ~Student()//析構函數
    {
        cout << "~Student()" << endl;
    }
private:
    string Schoolname;
    string Schooladdress;
};


void test(Student stu)//用于測試傳值過程中調用的構造析構函數的次數
{
    cout << endl << "function test is running" << endl << endl;
}

int main()
{
    Student s;
    test(s);//進行傳值調用測試函數
    return ;
}
           

在如上代碼中定義了Person和Student兩個類。類Student公有繼承自Person。他們分别有兩個string類對象成員。為了友善測試調用構造函數和析構函數的次數,在構造函數和拷貝構造函數中都列印出了對應的而标志。

執行結果:

Effective C++讀書筆記----自定義類型的傳參和傳回值問題

我們真正需要建立的就一個對象,調用一次構造一次析構就可以了。但是,在通過值傳遞調用測試函數的時候又調用了構造函數。而且,我們還沒有讨論兩個類的成員。基類個派生類的分别有兩個string類類型的成員。成員也是類類型,那麼,在建立對象的時候勢必也要調用類成員的構造函數。

基類和派生類各自有兩個類成員,也就是說在建立一個派生類對象,需要調用基類的構造函數、類成員的構造函數、以及它自身的構造函數。而在本例中,調用基類的構造函數建構基類部分的時候還要調用基類成員的構造函數。在根據實參拷貝出一個實參的過程中,調用了基類構造函數一次,基類的兩個成員變量分别調用構造函數,調用了3次構造函數才将基類部分建構出來,建構派生類的部分又調用了3次構造函數。總共就是調用6次構造函數和6次析構函數。這開銷還是挺大的,而且很浪費時間。

結果中沒有顯示出為類成員調用的構造函數和析構函數,因為string類的構造函數和析構函數不是我們實作的,沒有相關的調用成功的标志。

如果使用傳const引用來替代傳值,效率會高很多。因為不需要調用那系列的構造函數,而且使用const加以修飾,也可保證在調用函數中不會對他進行修改操作。除此之外,還有一個益處:防止對象被切割。如果傳遞的是一個派生類類型,而函數的參數是一個基類類型,那麼,傳進去的這個參數會被當做一個基類對象,它派生類的那部分會被“切割”掉。

  • 必須傳回對象時,不要妄想傳回引用。

直接傳回一個對象要進行額外的構造和析構,但是有時候這也是在所難免的。因為,想要傳回一個引用,那麼,首先得有一個已經存在了的對象,然後傳回這個對象的引用。

如果這個對象是通過參數傳進來的,那麼沒什麼問題。

如果這個對象是自己新建立的,如果是一個在棧上開辟的局部變量,那麼這就是傳回了棧空間的引用,相當于傳回了棧空間的位址,這是非法的。

如果這個對象是用new在堆上開辟的,将它傳回沒有沒什麼毛病,但是,在堆上建構這個對象時需要調用拷貝構造函數,這跟直接傳回對象調用構造函數也沒什麼差別隻是時間早晚的問題。而且,這還有一個潛在的危險,那就是需要調用者來管理這塊在堆上開辟的空間,調用者可能忘記釋放這塊空間,也可能對這塊空間釋放多次。并且,這個函數隻要調用一次就要為new一次。

可能會有人想到說把它設定為靜态變量,但是設定為靜态變量的話又引入了線程安全的問題。

繼續閱讀