再談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++程式設計師傾向於前者。
不過私有繼承所提供的特性確實比包含多。例如,假設類包含保護成員,則這樣的成員在派生類中是可用的,但在繼承層次機構外是不可用的。
如果使用組合獎這樣的類保護在另一類中,則後者將不是排成類,而是位於繼承層次結構之外,因此不能訪問保護成員。但通過繼承的到的將是派生類,因此他能夠訪問保護成員。
另一種需要使用私有繼承的情況是需要重新定義虛擬函式。派生類可以重新定義虛擬函式,但包含類不能。使用私有繼承,重新定義的函式將只能在類中使用,而不是公有的。