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 层,超过该层数通常需要对类进行重构。一个通用的测试是“是”与“有”,例如,正方形是矩形,但正方形有一组边。