6

简而言之,如何在 C++ 中为单级继承创建接口(出于简单和教学的原因)?我看到一些代码没有使用多态性,但基类包含一个虚拟抽象方法 ( virtual void TheMethod() = 0)。

现在从这个抽象类派生了一个类,使用纯虚方法,但是在随后的代码中,使用派生类的实例根本没有动态分配。

这是正确的方法吗?使用多态设计推断的开销如何?

我认为这是不可能的..这看起来更像是隐藏/重影基本方法,即使该方法是纯虚拟方法。

稍后编辑:感谢所有设法提供一些好的答案的人,我想强调一个由使用“动态分配”引起的严重错误,其含义是强调这种对象创建可能性是唯一与多态性兼容的. 很明显,这不是利用这种运行时调用行为的唯一方法(但可能是最常见的方法?),而是进一步澄清我最初的问题:

有没有办法强制程序员在不使用纯虚拟方法的情况下实现方法?我可能不合理的担忧是,是否打开了多态设计的大门是否在性能方面也有点沉重(讨论中的方法每秒有数千次这样的调用)。

甚至后来的编辑:使基础具有受保护的构造函数意味着它不能直接实例化(除了使用工厂或其他友好方式),这可以解决纯虚方法引起的影响之一的补偿。但是如何确保任何派生类仍然提供自己的方法实现呢?如果对关联的 vtable 的担心可能有点夸张真的没什么大不了的,我会坚持使用纯虚拟方法(因为 SFINAE 奇怪的重复模板模式更难被不熟悉的人阅读和理解)最少的中级 C++ 程序员 - 像我一样 :) )。

4

4 回答 4

5

您不必动态分配对象以多态地使用它:

struct base {
    virtual void foo() = 0;
};

struct derived : base {
    virtual void foo() {
        // do stuff
    }
};

void f(base& object) {
    object.foo();
}

int main() {
    derived object; // no dynamic allocation at all
    f(object); // polymorphism happens here
}
于 2012-08-24T08:26:23.737 回答
5

出于教学原因,如果目标是了解如何在 C++ 中实现多态对象并测试已创建的类型,则不需要动态分配。然而,在实际应用程序中,它可能会是,因为使用多态的主要原因是因为具体类型要到运行时才能知道。

请注意,在 C++(以及几乎所有其他支持它的语言)中,多态性需要引用语义,而不是 C++ 默认使用的值语义。通常,设计用作基类的类支持复制和赋值(可能通过虚clone()函数除外)。

关于开销:与什么相比?调用虚函数通常比调用非虚函数更昂贵。但是如果你使用虚函数,那是因为你需要运行时调度;使用其他一些机制来模拟这一点可能会更加昂贵。

于 2012-08-24T08:43:33.043 回答
3

是的,正如其他人所说,您基本上使用具有纯虚拟(抽象)成员函数且没有数据成员的类。在实现这个接口的时候,自然要提供这些方法。

另一方面,这与动态分配无关。您是否拥有自动对象(即堆栈)或动态对象(即堆)与您如何使用它们无关,包括多态性。你的意思是动态绑定

现在,说了这么多,您可以在不使用模板的动态绑定(即“多态性”)的情况下实现接口。基本上,您将使用 SFINAE+CRTP 通过从模板类私有继承来检查给定的成员函数是否存在。基本上,您的父类(不包含虚拟成员)template <typename T> class FooIface;(继承自 as )将通过尝试调用它来class Foo : private FooIface<Foo>确保T具有成员函数。foo使用元编程技巧,您还可以确保foo具有正确的类型。

但这可能太麻烦且难以阅读。抽象基类是常用的方法。

于 2012-08-24T08:35:15.813 回答
1

C++中没有接口
的概念, 您只能使用抽象类来模拟行为。 抽象类是一个具有至少一个纯虚函数的类,一个不能创建抽象类的任何实例,但您可以创建指向它的指针和引用。此外,从抽象类继承的每个类都必须实现纯虚函数才能创建它的实例。

动态分配多态无关!

动态分配定义了对象将被分配到哪里,并且与为自动变量提供的隐式内存管理不同,该对象应具有显式内存管理。

多态性意味着一个事物的多种形式。这是在类中具有不同行为的相同函数。您可以有一个指向堆栈上的对象的基类指针,并且仍然具有多态行为。

于 2012-08-24T08:25:54.703 回答