我知道我们必须至少添加一个纯虚拟成员函数,并且可以添加一个静态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;
};
答案 0 :(得分:3)
C++
接口不是标准构造,只是class
(或struct
)作者已决定应该是一个接口。因此,关于什么应该和不应该进入它的约定。
根据经验,界面中的唯一内容应该是纯虚函数。这样,您不提供任何实现细节,而只是提供实现该接口的对象应具有的功能的描述。
在C ++中,如果至少有一个函数是纯虚函数,则类是 abstract 。因此,它们可能以任何适合作者品味的方式包含数据成员和具体功能。
答案 1 :(得分:2)
你在这里混淆了各种各样的概念。
一个抽象类是一个至少有一个函数是纯虚函数的类,这意味着它有一个带有= 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%不想要他们的课程中闲逛......
答案 2 :(得分:0)
[特定于OP现已发布的示例的答案]。
你的班级有缺点。在它当前的形式中它不能真正代表一个圆(因为你提供两个自由度来计算面积)。您也不能轻易地表示不规则多边形。
我认为你应该做的是有一个界面,通常是class
版本virtual double getArea() const = 0;
。称之为Shape
。然后重命名继承自Rectangle
。
Shape