1. 程式人生 > >Effective C++讀書筆記----自定義型別的傳參和返回值問題

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 0; }

在如上程式碼中定義了Person和Student兩個類。類Student公有繼承自Person。他們分別有兩個string類物件成員。為了方便測試呼叫建構函式和解構函式的次數,在建構函式和拷貝建構函式中都打印出了對應的而標誌。
執行結果:
這裡寫圖片描述
我們真正需要建立的就一個物件,呼叫一次構造一次析構就可以了。但是,在通過值傳遞呼叫測試函式的時候又呼叫了建構函式。而且,我們還沒有討論兩個類的成員。基類個派生類的分別有兩個string類型別的成員。成員也是類型別,那麼,在建立物件的時候勢必也要呼叫類成員的建構函式。
基類和派生類各自有兩個類成員,也就是說在建立一個派生類物件,需要呼叫基類的建構函式、類成員的建構函式、以及它自身的建構函式。而在本例中,呼叫基類的建構函式構建基類部分的時候還要呼叫基類成員的建構函式。在根據實參拷貝出一個實參的過程中,呼叫了基類建構函式一次,基類的兩個成員變數分別呼叫建構函式,呼叫了3次建構函式才將基類部分構建出來,構建派生類的部分又呼叫了3次建構函式。總共就是呼叫6次建構函式和6次解構函式。這開銷還是挺大的,而且很浪費時間。
結果中沒有顯示出為類成員呼叫的建構函式和解構函式,因為string類的建構函式和解構函式不是我們實現的,沒有相關的呼叫成功的標誌。
如果使用傳const引用來替代傳值,效率會高很多。因為不需要呼叫那系列的建構函式,而且使用const加以修飾,也可保證在呼叫函式中不會對他進行修改操作。除此之外,還有一個益處:防止物件被切割。如果傳遞的是一個派生類型別,而函式的引數是一個基類型別,那麼,傳進去的這個引數會被當做一個基類物件,它派生類的那部分會被“切割”掉。

  • 必須返回物件時,不要妄想返回引用。

直接返回一個物件要進行額外的構造和析構,但是有時候這也是在所難免的。因為,想要返回一個引用,那麼,首先得有一個已經存在了的物件,然後返回這個物件的引用。
如果這個物件是通過引數傳進來的,那麼沒什麼問題。
如果這個物件是自己新建立的,如果是一個在棧上開闢的區域性變數,那麼這就是返回了棧空間的引用,相當於返回了棧空間的地址,這是非法的。
如果這個物件是用new在堆上開闢的,將它返回沒有沒什麼毛病,但是,在堆上構建這個物件時需要呼叫拷貝建構函式,這跟直接返回物件呼叫建構函式也沒什麼區別只是時間早晚的問題。而且,這還有一個潛在的危險,那就是需要呼叫者來管理這塊在堆上開闢的空間,呼叫者可能忘記釋放這塊空間,也可能對這塊空間釋放多次。並且,這個函式只要呼叫一次就要為new一次。
可能會有人想到說把它設定為靜態變數,但是設定為靜態變數的話又引入了執行緒安全的問題。