隱藏.cpp裡一切細節的範例程式

在C++中,分離類別實作和類別介面的做法有幾個優點:

  1. 編譯時可以減少因為修改而牽動的檔案數量,大幅降低檔案重新編譯的規模(Effective C++ 2nd #37, 3rd #31)
  2. 在提供別人編譯所需要的檔案時,可以成功讓人編譯,又不會導致自己的Source Code被看光,而破壞類別的封裝性,或被修改程式碼,破壞了類別的整體概念性。

基於以上兩點優點,一定要學下這一招。
去翻了一下Effective C++ 2rd #37,以下是以它的程式碼為基礎,所做的練習。




程式執行結果:

阿童木生於2003/04/07,目前住在日本

原本的程式


主程式

#include <iostream>
#include "Person.h"
#include "date.h"
#include "address.h"

using namespace std;

int main()
{
    Address theAddr("日本");
    Date    theDate("2003/04/07");
    Person  thePersonOne("阿童木", theDate, theAddr);
 
    cout << thePersonOne.name() << "生於" <<
            thePersonOne.birthDate() << "目前住在" <<
            thePersonOne.address() << endl;
    return 0;
}

//Person.h v1.0

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

class Person
{
 std::string theName;
 Date theBirthDate;
 Address theAddress;
public:
    Person();
    Person(const std::string& name,
           const Date& birthday,

    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
    //...
};

//Person.cpp v1.0

#include "Person.h"

Person::Person(){}
Person::Person(const std::string& name, const Date& birthday, const Address& addr)
{
    theName = name;
    theBirthDate = birthday;
    theAddress = addr;
}

std::string Person::name()      const{ return theName;             }
std::string Person::birthDate() const{ return theBirthDate.date(); }
std::string Person::address()   const{ return theAddress.addr();   }

//address.h v1.0

#include <string>

class Address
{
    std::string theAddr;
public:
    Address();
    Address(const std::string& addr);  
    std::string addr() const;
    void set(const std::string);
};

//address.cpp v1.0

#include "address.h"

Address::Address(){}
Address::Address(const std::string& addr){ theAddr = addr; }

std::string Address::addr() const
{
    return theAddr;
}

//date.h v1.0

#include <string>

class Date
{
    std::string theDate;
public:
    Date();
    Date(const std::string& date);
    std::string date() const;
    void Set(const std::string );
};

//date.cpp v1.0

#include "date.h"

Date::Date(){}
Date::Date(const std::string& date){ theDate = date; }

std::string Date::date() const
{
    return theDate;
}

隱藏Person實作的成員變數

以下的做法,只要在專案內加入Person.o、PersonImpl.o取代相對應的.cpp檔。

//Person.h v1.0.1

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

struct PersonImpl;  //要要做一個類別,並且先在這宣告
                   //因為它的.h和.cpp檔不給,只給它的.obj檔,以隔壁實作(不用重新編繹了)
class Person
{ 
//  std::string theName;      //這三行註解是要被隱藏的地方
//  Date theBirthDate;        //原本的實作,之後要刪掉!
//  Address theAddress;       //在此顯示只是為了做對照
    PersonImpl* pImpl;        //宣告一個實作類別的指標(或參考也行,就是不可以是實體)
public:
    Person();
    Person(const std::string& name, 
           const Date& birthday,
           const Address& addr);

    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
    //...
};

//Person.cpp v1.0.1

//之後的這個檔案也沒有要給
#include "Person.h"
#include "PersonImpl.cpp"        //在這include,就可以不用給PersonImpl.cpp

Person::Person(){}

Person::Person(const std::string& name, const Date& birthday, const Address& addr)
{
    pImpl = new PersonImpl();   //要在這new一個PersonImpl的物件
    pImpl->theName;
    pImpl->theBirthDate;
    pImpl->theAddress;
}

std::string Person::name()      const{ return pImpl->theName;             }
std::string Person::birthDate() const{ return pImpl->theBirthDate.date(); }
std::string Person::address()   const{ return pImpl->theAddress.addr();   }

//PersonImpl.cpp

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

struct PersonImpl
{
    std::string theName;
    Date theBirthDate;
    Address theAddress;
};

以上,再配合typedef和適當命名的副程式,就可以隱藏一切的基礎類別,而使用自己訂的字來當程式碼。

沒有留言:

張貼留言

(什麼是留言欄訊息?)