1. 程式人生 > >再談C++中的has-a關係(通過複合塑模出has-a 明智而審慎地使用Private繼承)

再談C++中的has-a關係(通過複合塑模出has-a 明智而審慎地使用Private繼承)

                     

今天就再談談通過複合、私有繼承來實現has-a關係。

由於翻譯差異,我們姑且認為“包含”和“複合”是一個概念!

一、 使用包含實現has-a關係

這種關係的實現無非就是一個類是另外一個類的成員而已。

valarray類有標頭檔案valarray支援,用於處理數值。 下面我們實現自己的Student類,包含valarray類的成員。

class Student{private:    string name;    valarray<double> scores;    ...};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上訴程式碼顯示:Student類的成員函式可以使用string和valarray類的公有介面來訪問和修改name和scores物件。但是在類的外面不允許這樣做,只能通過Student類的公有介面訪問name和scores. 這就是我們所說的Student獲得了其成員物件的實現而沒有獲得介面。 因此,可以如下使用被包含物件的介面:

double Student::Average() const{    if(scores.size() > 0)        return scores.sum()/scores.size();    else        return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

二、使用私有繼承實現has-a關係

c++還有另一種實現has-a關係的途徑—-私有繼承。使用私有繼承,基類的公有成員和保護成員都將成為派生類的私有成員。這意味著基類方法將不會成為派生物件公有介面的一部分,但可以在派生類的成員函式中使用它們。 使用公有繼承,基類的公有方法將成為派生類的公有方法。簡而言之,派生類將繼承基類的介面,這是is-a關係的一部分。使用私有繼承,基類的公有方法將成為派生類的私有方法。簡而言之,派生類不能繼承基類的介面。正如從被包含物件中看到的,這種不完全繼承是has-a關係的一部分。 因此私有繼承提供的特性與包含相同:獲得實現,但不獲得介面。所以,私有繼承也可以用來實現has-a關係。程式碼如下:

class Student : private std::string, private std::valarray<double>{public:    ...};
  • 1
  • 2
  • 3
  • 4
  • 5

與使用包含的第一個區別: 包含版本提供了兩個被顯示命名的物件成員,而私有繼承提供了兩個無名稱的子物件成員。

與使用包含的第二個區別: 私有繼承是使用類名而不是成員名來標識建構函式。 包含的建構函式:

Student(const char* str, const double *pd, int n):name(str),scores(pd,n){}
  • 1
  • 2
  • 3
  • 4

私有繼承的建構函式:

Student(const char* str, const
double *pd, int n):std::string(str),std::valvarray<double>(pd, n){}
  • 1
  • 2
  • 3
  • 4

三、使用包含還是私有繼承

由於既可以使用包含,也可以使用私有繼承來建立has-a關係。大多數c++程式設計師傾向於前者。

不過私有繼承所提供的特性確實比包含多。例如,假設類包含保護成員,則這樣的成員在派生類中是可用的,但在繼承層次機構外是不可用的。

如果使用組合獎這樣的類保護在另一類中,則後者將不是排成類,而是位於繼承層次結構之外,因此不能訪問保護成員。但通過繼承的到的將是派生類,因此他能夠訪問保護成員。

另一種需要使用私有繼承的情況是需要重新定義虛擬函式。派生類可以重新定義虛擬函式,但包含類不能。使用私有繼承,重新定義的函式將只能在類中使用,而不是公有的。