c++筆記 降低標頭檔間的編譯依存關係

來源:互聯網
上載者:User

1.編譯依存關係
    某些時候只修改了某個類的private,結果卻有一大堆檔案需要編譯。問題出在c++並沒有把“將介面從實現中分離”這事做好。Class的定義式不只詳細敘述了class介面,還包括十足的實現細目。例如:

class Perpson {public:    std::string name() const;    std::string birthData() const;    std::string address() const;...private:    std::string theName;    //實現細目    Date theBirthDate;    Address theAddress;};

在Person定義檔案的最上方很可能存在這樣的東西:

#include <string>#include "date.h"#include "address.h"

    不幸的是,這麼一來便是在Person定義檔案和其含入檔案之間形成了一種編譯儲存關係。如果這些標頭檔中有任何一個被改變,或者這些檔案檔案所做事的其他標頭檔有任何改變,那麼每一個含入Person class的檔案就得重新編譯。這樣的連串編譯依存關係會對許多項目造成難以形容的災難。
    考慮加上前置聲明,以去掉#include包含的標頭檔:

class Date;class Address;

這樣做有一個困難,編譯器必須在編譯期間知道對象的大小。考慮這個:

int main(){    int x;    Person p;    ...}

    當編譯器看到x的定義式,它知道必須分配多少記憶體才夠持有一個int。沒問題,每個編譯器都知道一個int有多大。當編譯器看到p的定義式,它也知道必須分配空間以旋轉一個Person,但它如何知道一個Person對象有多大呢?唯一的辦法就是詢問class定義式。然而如果class定義式可以合法地不列出實現細目(使用前置聲明),編譯器如何知道該分配多少空間?

2.Handle class
    我們可以使用pimpl,“將對象實現細目隱藏於一個指標背後”的遊戲。針對Person我們可以這樣做:把Person分割為兩個class,一個只提供介面,另一個負責實現該介面。負責實現的那個類取名為PersonImpl,Person定義如下:

#include <string>#include <memory>class PersonImpl;class Date;class Address;class Person {public:    std::string name() const;    ...private:    std::tr1::shared_ptr<PersonImpl> pImpl; //指標,};

    上面的這般設計常被稱為pimpl idiom(pimpl 是“pointer to implementation”),像Person這樣使用pimpl idiom的class,往往被稱為Handle class。這樣的設定計之下,Person的客戶就完全與Dates等實現細目分離,真正實現了“介面與實現分離”。這個分離的關鍵在於以“聲明的依存性”替換“定義依存性”,其實每一件事都源自這幾個簡單的設計策略:
1)如果使用object references 或object pointers可以完成任務,就不要使用objects。
2)如果能夠,盡量以class聲明式替換class定義式。注意,當你聲明一個函數而它用到某個class時,你並不需要該class的定義;縱使以by value方式傳遞該類型的參數(或傳回值)亦然:

class Date;                     //class 聲明式Date today();                   //沒問題--這裡並不需要Date的定義式void clearAppointments(Date d); 

3)為聲明和定義提供不同的標頭檔

3.Interface class 
令Person成為一種特殊的abstract base class,稱為Interface class。如下:

class Person {public:    static std::tr1::shared_ptr<Person> create();   //使用一個Factory 方法建立真實對像    virtual ~Person();    virtual std::string name() const = 0;    virtual std::string birthDate() const = 0;    virtual std::string address() const = 0;    }
class RealPerson: public Person {...}static std::tr1::shared_ptr<Person> create {    return std::tr1::shared_prt<Person>(new RealPerson());}

注,將Factory 方法提出來,放到單獨的工廠類中,效果應該更好。

摘自Effective c++

PS:關於pimpl的實現方式
pimpl可以以下面的方式實現:

//Person.h#include <memory>class Person{public:Person(void);~Person(void);private:class PersonImpl;std::tr1::shared_ptr<PersonImpl> pImpl;};//Person.cpp#include "Person.h"class Person::PersonImpl {};Person::Person(void){pImpl = std::tr1::shared_ptr<PersonImpl>(new PersonImpl);}Person::~Person(void){}

PersonImpl定義在Person域中,外部不再可見

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.