我知道我们必须至少添加一个纯虚拟成员函数,添加静态const和void返回方法是可以的(也需要虚拟析构函数),但还有什么可以添加而不会出错的吗?
示例:
// Base class
class Shape
{
public:
// pure virtual function providing interface framework.
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
C++
接口不是一个标准构造,它只是作者决定应该作为接口的class
(或struct
)。因此,这取决于惯例,什么应该和不应该进入其中
根据经验,接口中只有纯虚拟函数。通过这种方式,您不提供任何实现细节,而只是对实现该接口的对象应该具有的功能的描述。
在C++中,如果至少有一个函数是纯虚拟的,则类是抽象。因此,它们可能以任何适合作者口味的方式包含数据成员和具体功能。
您在这里将各种概念拼凑在一起。
抽象类是指至少有一个函数是纯虚拟的,这意味着它有一个后缀为= 0
的虚拟函数,表明类本身不能实例化,派生类必须指定该函数的实现。
C++语言对抽象类的唯一限制是它不能被实例化——它仍然可以从基派生,并且具有静态和非静态的数据和函数成员,它可以提供虚拟函数的实现,甚至是纯虚拟函数(其基本实现只能由显式调用调用,通常来自派生类对同一函数的覆盖),提供一些默认或附加行为。
至关重要的是,使用抽象基类作为"接口"意味着强制客户端代码使用动态内存分配和虚拟调度。这意味着到处都是(共享的)指针,以及一组特殊的编程实践和复杂性,这在此类模型中很常见,但对于经验不足的开发人员来说,这可能会让他们感到不舒服或不知所措。另一种选择是使用一种非常不同风格的"接口"类,该类具有值语义,采用了指向实现的指针(pImpl)习惯用法,该习惯用法将一些派生类管理内部化-这对库实现者来说往往更痛苦-更乏味的小转发函数-但对用户来说更容易。
需要考虑的一个准则是非虚拟接口(NVI),这表明类中的公共函数应该是调用私有虚拟函数的非虚拟函数:这样,可以修改基类,以提供一些在调用该函数时调用的前代码和后代码,以及在向派生类的实现进行虚拟调度之前调用的代码,例如对基类进行调试、定时检测、线程安全等,并使其支持整个类层次结构。
基类中可能需要更改的所有内容都可能需要重新编译使用基类的所有客户端代码(即包括其标头)。在企业环境中,对低级别库的头的更改可能会迫使重新编译非常耗时,而且作为库开发人员,您通常只能向共享对象/动态库分发更新,但无法控制客户端何时重新编译其依赖代码以从头中获取更改。在这种情况下,如果基类没有指定任何可以移动到实现类中的细节,那么您可以保留影响库中自包含的更改的最大自由。作为指导原则,您应该避免任何私人成员,除非是那些实现如上所述的非虚拟接口的成员,以及任何与故意限制类的使用有关的成员(例如,使其"不可复制")。
考虑到你的Shape类,具有相同"界面"的最灵活的基类是:
class Shape
{
public:
virtual ~Shape(); // support dispatch to derived destructors
// pure virtual function providing interface framework.
int getArea() const;
void setWidth(int w);
void setHeight(int h);
private:
virtual void v_setWidth(int) = 0;
virtual void v_setHeight(int) = 0;
};
实现文件可能以开头
void Shape::setWidth(int n) { v_setWidth(n); }
void Shape::setHeight(int n) { v_setHeight(n); }
派生类可以添加实际的宽度和高度成员,也可以选择存储中心和水平/垂直范围,或者根本没有宽度或高度成员的多边形点列表。。。您已经允许派生类有更多的多样性,而没有基类数据成员可能闲置。另一方面,如果99%的派生类只需要int
宽度和高度成员,则可以提供一个带有class Shape_WH : public Shape { protected: int width, height; };
的不同标头,以简化此类派生类的创建,而无需将这些详细信息强制到客户端翻译单元中,也无需将它们挂在1%不需要它们的类中。。。。
[针对OP现在发布的示例的答案]。
你们班有缺点。在其当前形式中,它不能真正表示圆(因为您提供了两个自由度来计算面积)。您也无法轻松地表示不规则多边形。
我认为您应该做的是有一个接口,按照惯例,它是一个包含virtual double getArea() const = 0;
的class
。称之为Shape
。然后将从Shape
继承的类重命名为Rectangle
。