引言:C++的一個主要目的是代碼重用,提高效率,公有繼承是實現這個目的的一種機制。還有其他的機制,本部分主要介紹其他代碼重用方法,一種是包含、組合或層次化,另一種是私人或保護繼承,通常組合、私人繼承和保護繼承喲國語實現has-a關係,即新的對類將包含另一個類的對象。還有一種就是和函數模板對應的類模板。
1.包含對象成員的類:
包含對象的類就是這樣一種類:類中包含了這樣的一個類成員:本身是另一個類的對象。這種方法稱為包含、組合或層次化。
C++和約束:C++包含讓程式員能夠限制程式結構的特性——使用explicit防止單參數建構函式的隱式轉換,使用const限制方法修改資料等等。這樣做的根本原因是:在編譯階段出現錯誤要比在運行階段出現錯誤要好,要優。
初始化順序:當初始化列表包含多重專案時,這些項目被初始化的順序為它們被聲明的順序,而不是它們在初始化列表中的順序。例如:
Student(const char *str,const double *pd,int n):scores(pd,n),name(str){}
則name成員仍將首先初始化,因為在類定義中它首先被聲明。
介面和實現:使用公有繼承是,類可以繼承介面,可能還有實現(基類的純虛函數提供介面,但不提供實現)。獲得介面是is-a關係的組成部分。而是用組合,類可以獲得實現,但不能獲得介面。不繼承介面是has-a關係的組成部分。
2.私人繼承和保護繼承:
私人繼承:
私人繼承,也是實現has-a關係的一種途徑。使用私人繼承,基類的公有成員和保護成員都將成為衍生類別的私人成員。這就意味著基類方法將不會成為衍生類別對象公有介面的一部分,但可以在衍生類別的成員函數中使用它們。
介面問題:使用公有繼承,基類的公有方法將成為衍生類別的公有方法,。簡而言之,衍生類別將繼承基類的介面;這是is-a關係的一部分。使用私人繼承,基類的公有方法就成為衍生類別的私人方法,簡而言之,衍生類別不繼承基類的介面。這種不完全繼承是has-a的關係的一部分。
使用私人繼承類將繼承實現。
包含是將對象作為一個命名的成員對象添加到類中,而私人繼承將對象作為一個未被命名的繼承對象添加到類中。一般用術語子物件來表示通過繼承或包含添加的對象。
從上面可知,私人繼承的特性和包含(組合)相同,所以,私人繼承也可以用來實現has-a關係。
訪問基類的方法:使用包含時將使用對象名來調用方法,而是用私人繼承時將使用類名和範圍解析操作符::來調用方法
訪問基類對象:一般使用強制類型轉換。例如:Student類是string類派生而來的:
cosnt string & Student::Name()const
{
return (const string &) *this;
}
訪問基類的友元函數:用類名顯示地限定函數名不適合與友元函數,因為友元不是類成員,不過,可以通過顯式地轉換為基類來調用正確的函數。例如,對於下面的友元函數定義:
ostream & operator<<(ostream & os,Student & stu)
{
os <<"Scores for " <<(const string &) stu <<":\n";
...
}
如果pstu是一個Student對象,則下面的語句:
cout<<pstu;將調用上述函數,stu將是指向pstu的引用,而os將是指向cout的引用。下面的代碼:
os<<"Scores for " << (const string &) stu <<":\n"; 顯式地將stu轉換為string對象的引用,這與operator<<(ostream &,cosnt String &)函數匹配
引用stu不會自動轉換為sting引用。根本原因在於,在私人繼承中,在不進行顯式類型轉換的情況下,不能將指向衍生類別的引用或指標賦給基類應用或指標。
不過,即使這個例子使用的是公有繼承,也必須使用顯示類型轉換。原因之一是,如果不使用類型轉換,下面代碼將與友元函數原型匹配,從而導致遞迴調用:os<<stu; 另一個原因是,如果使用的多重繼承,編譯器無法確定應轉換為哪個基類,如果兩個基類都提供了函數operator<<()。
3.多重繼承:
多重繼承,顧名思義,就是使用多個基類的繼承(multiple inheritance,MI),描述的是有多個直接基類的類。
4.類模板:
定義:開頭用 template <class Type> 或者 template <typename Type>,模板的具體實現就是執行個體化或具體化。
例如建立一個堆棧類模板樣本:
#pragma once//
#include <iostream>
using namespace std;
template <class Type>
class Stack
{
private:
enum{MAX = 10};
Type items[MAX];
int top;
public:
Stack();
bool IsEmpty();
bool IsFull();
bool Push(const Type &item);
bool Pop(Type & item);
};
template <class Type>
Stack<Type>::Stack()
{
top = 0;
}
template<class Type>
bool Stack<Type>::IsEmpty()
{
return top == 0;
}
template <class Type>
bool Stack<Type>::IsFull()
{
return top == MAX;
}
template <class Type>
bool Stack<Type>::Push(const Type &item)
{
if(top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}
template <class Type>
bool Stack<Type>::Pop(Type & item)
{
if(top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
指標類型類模板:既然可以將內建類型或類對象用作類模板的類型,那麼指標可以嗎?答案是肯定的。但是,要注意正確使用指標。
使用指標堆棧的方法之一是,讓調用程式提供一個指標數組,其中每隔指標都指向不同的字串。把這些指標都放在指標堆棧是有意義的,因為每個指標都將指向不同的字串。注意,建立不同指標是調用程式的職責,而不是堆棧的職責。堆棧的認為是管理指標,而不是建立指標。
模板還可以帶參數,例如:template <class Type,int n>,裡面的 int n稱為非類型或運算式參數,但是運算式參數有一定的限制。只可以是整型、枚舉、引用或指標。還可以使用多個型別參數,例如 template <class T1,class T2>
預設類型模板參數:類模板的一項特性是,可以為型別參數提供預設值:
temp <class T1,class T2 = int>
class Topo
{
...
};
這樣,如果省略了T2的值,編譯器將使用int:
Topo<double,double> m1; //T1 is double,T2 is double
Topo<double> m2; //T1 is double ,T2 is int
標準模板庫將此使用該特性,將預設類型設定為類
雖然可以為類模板型別參數提供預設值,但不能為函數模板參數提供預設值。不過,可以為非型別參數提供預設值,這對於函數模板和類模板都都是適用的。
模板的其他特性:可用作結構、類或模板類的成員;可將模板用作參數
模板類和友元:模板聲明也可以有友元。模板的友元分為3類:
● 非模板友元
● 約束(bound)模板友元,即友元的類型取決於類被執行個體化時的類型
● 非約束(unbound)模板友元,即友元的所有具體化都是類的每一個具體化的友元