C++ 程式設計/類/抽象類
從概念上講,抽象類是不能被例項化的類,通常實現為包含一個或多個純虛擬函式(抽象函式)的類。
純虛擬函式是必須被任何具體的(即非抽象的)派生類重寫。這在宣告中用成員函式宣告中的語法" = 0" 表示。
- 示例
class AbstractClass {
public:
virtual void AbstractMemberFunction() = 0; // Pure virtual function makes
// this class Abstract class.
virtual void NonAbstractMemberFunction1(); // Virtual function.
void NonAbstractMemberFunction2();
};
通常,抽象類用於定義一個實現,並且旨在被具體的類繼承。它是一種在類設計者和該類的使用者之間強制執行契約的方式。如果我們希望從抽象類建立一個具體的類(可以例項化的類),我們必須宣告並定義基類每個抽象成員函式的匹配成員函式。否則,如果基類的任何成員函式未定義,我們將建立一個新的抽象類(這在某些情況下可能有用)。
有時我們使用“純抽象類”這個短語,指的是一個類,它只包含純虛擬函式(沒有資料)。介面的概念在 C++ 中對映到純抽象類,因為 C++ 中沒有像 Java 中那樣的“介面”構造。
- 示例
class Vehicle {
public:
explicit
Vehicle( int topSpeed )
: m_topSpeed( topSpeed )
{}
int TopSpeed() const {
return m_topSpeed;
}
virtual void Save( std::ostream& ) const = 0;
private:
int m_topSpeed;
};
class WheeledLandVehicle : public Vehicle {
public:
WheeledLandVehicle( int topSpeed, int numberOfWheels )
: Vehicle( topSpeed ), m_numberOfWheels( numberOfWheels )
{}
int NumberOfWheels() const {
return m_numberOfWheels;
}
void Save( std::ostream& ) const; // is implicitly virtual
private:
int m_numberOfWheels;
};
class TrackedLandVehicle : public Vehicle {
public:
TrackedLandVehicle ( int topSpeed, int numberOfTracks )
: Vehicle( topSpeed ), m_numberOfTracks ( numberOfTracks )
{}
int NumberOfTracks() const {
return m_numberOfTracks;
}
void Save( std::ostream& ) const; // is implicitly virtual
private:
int m_numberOfTracks;
};
在這個例子中,Vehicle 是一個抽象基類,因為它具有一個抽象成員函式。WheeledLandVehicle 類從基類派生。它還包含所有輪式陸地車輛共有的資料,即車輪數量。TrackedLandVehicle 類是 Vehicle 類的另一個變體。
這是一個有點人為的例子,但它確實展示瞭如何在類層次結構中共享實現細節。每個類都進一步完善了一個概念。這並不總是實現介面的最佳方式,但在某些情況下它非常有效。作為指導方針,為了便於維護和理解,您應該嘗試將繼承限制在不超過 3 個級別。通常,最佳的類集是使用純虛抽象基類來定義一個通用介面。然後使用一個抽象類來進一步完善一組具體類的實現,最後定義一組具體類。
一個抽象類是一個專門設計用於用作基類的類。抽象類至少包含一個純虛擬函式。透過在類宣告中使用純說明符 (= 0) 來宣告純虛擬函式。
以下是一個抽象類的示例
class AB {
public:
virtual void f() = 0;
};
函式 AB::f 是一個純虛擬函式。函式宣告不能同時具有純說明符和定義。
抽象類不能用作引數型別、函式返回型別或顯式轉換的型別,也不能用於宣告抽象類的物件。它可以用來宣告指向抽象類的指標和引用。
抽象類是指其中成員函式有宣告但沒有定義的類。C++ 中表達此概念的方法是將成員函式宣告賦值為零。
- 示例
class PureAbstractClass
{
public:
virtual void AbstractMemberFunction() = 0;
};
純抽象類僅具有抽象成員函式,沒有資料或具體成員函式。通常,純抽象類用於定義一個介面,並且旨在被具體的類繼承。它是一種在類設計者和該類的使用者之間強制執行契約的方式。該類的使用者必須宣告一個匹配的成員函式才能編譯該類。
- 純抽象類的使用示例
class DrawableObject
{
public:
virtual void Draw(GraphicalDrawingBoard&) const = 0; //draw to GraphicalDrawingBoard
};
class Triangle : public DrawableObject
{
public:
void Draw(GraphicalDrawingBoard&) const; //draw a triangle
};
class Rectangle : public DrawableObject
{
public:
void Draw(GraphicalDrawingBoard&) const; //draw a rectangle
};
class Circle : public DrawableObject
{
public:
void Draw(GraphicalDrawingBoard&) const; //draw a circle
};
typedef std::list<DrawableObject*> DrawableList;
DrawableList drawableList;
GraphicalDrawingBoard drawingBoard;
drawableList.pushback(new Triangle());
drawableList.pushback(new Rectangle());
drawableList.pushback(new Circle());
for(DrawableList::const_iterator iter = drawableList.begin(),
endIter = drawableList.end();
iter != endIter;
++iter)
{
DrawableObject *object = *iter;
object->Draw(drawingBoard);
}
請注意,這是一個有點人為的例子,可繪製物件沒有完全定義(沒有建構函式或資料),但它應該能讓您大致瞭解定義介面的強大功能。一旦物件被構造,呼叫介面的程式碼不知道被呼叫物件的任何實現細節,只知道介面的實現細節。物件GraphicalDrawingBoard 是一個佔位符,用於表示將繪製物件的實體,例如影片記憶體、繪圖緩衝區、印表機。
請注意,在純抽象基類中新增具體成員函式和資料的誘惑很大。這必須被抵制,通常它表明介面沒有很好地分解。資料和具體成員函式往往暗示特定的實現,因此可以從介面繼承,但不應該成為該介面。相反,如果具體類之間存在某些共性,則建立一個從純抽象類繼承其介面並定義具體類的通用資料和成員函式的抽象類是有效的。應謹慎決定是使用繼承還是聚合。太多的繼承層會導致類的維護和使用變得困難。通常,接受的繼承層數最多為 3 層,超過該層數通常需要對類進行重構。一個通用的測試是“是”與“有”,例如,正方形是矩形,但正方形有一組邊。