1

请考虑一个像这样的纯虚拟类:

class Foo {
    public: virtual int FooBar() = 0;
};

Java 开发人员会将这样的东西称为“接口”。通常,您然后编程到该接口,例如(不好的例子,对不起):

class Bar {
    public: Foo f;
};

class Child : public Foo {
    public: int FooBar() { return 1; }
};

Bar b;
b.foo = Child();

这显然在 C++ 中不起作用,因为类 Foo 的实例是在构造时创建的(但这是不可能的,因为 Foo 是一个抽象类)。

在 C++ 中“编程到接口”有什么模式吗?

编辑:澄清了这个例子,对不起。

4

6 回答 6

3

为了像在 Java 中一样使用多态性,您需要使用指针和引用,就像在 Java 中一样。在 Java 中,一切要么是原始类型,要么是引用:当你用Foo f;Java 编写代码时,你会得到一个引用。在 C++ 中,你得到一个object

但是,与 Java 不同,C++ 中的接口不必涉及虚函数和基类。标准库中使用的迭代器接口就是一个很好的例子。

于 2012-08-14T10:24:54.940 回答
3

如果您需要指向或引用接口(或抽象类)的实现的东西,您可以使用对该接口的引用的指针:

class Bar {
public:
    Bar( Foo& af ) : f( af )
    {}
private: 
    Foo& f;
};

如果你想使用一个引用Foo& f,一个指针Foo* f或一个引用或指向 const 实现 ( const Foo& fof const Foo* f) 的指针,甚至是一个指向 const 实现 ( const Foo* const f) 的 const 指针,取决于你的要求。

如果可能,请使用引用,用于从外部传递引用实现的组合(如我的示例中)。如果对象更多是聚合并且由包含对象本身构造,则使用指针或智能指针。

更新: 到目前为止,没有其他人提到过,如果要动态分配实现接口的对象,则基类必须具有虚拟析构函数,否则即使使用 smart 也会调用未定义的行为指针。请注意,智能指针可能很方便,但它们并不是万能的。您仍然应该牢记一些所有者层次结构,否则您最终会遇到智能指针无法轻松解决的循环。

于 2012-08-14T10:23:40.670 回答
3

您的数据成员f应声明为指向的指针,Foo或者更好的是,声明为指向 的某种形式的智能指针Foo,例如shared_ptr<Foo>.

您可以使用对 的引用Foo,但这会使事情复杂化:您的类将需要一个显式构造函数并且不可复制或可分配,除非您显式实现这两个成员函数。

于 2012-08-14T10:23:55.977 回答
2

我认为您需要继承“接口”,而不是使用组合,不是吗?

像这样

class Bar: public Foo
{
private: 
    virtual int Bar() { /* ... */ }
};

这就是我对“程序到界面”的理解。


编辑:啊哈,看看你的编辑,你真的需要在这里指点。这是等效的 C++ 代码:

class Foo {
    public: virtual int Bar() = 0;
};

class Bar {
    //.........v
    public: Foo* f;
};

class Child : public Foo {
    public: int Bar() { return 1; }
};

Bar b;
b.f = new Child();

但是你需要照顾新分配的内存Child()。为了避免这种情况并使代码绝对(逻辑上)等效,您需要智能指针,对于这种情况 - a shared_ptr( C++11 , boost )

于 2012-08-14T10:22:24.137 回答
0

你做的一切都是对的,只有一个例外。您需要从接口派生,而不是将其作为成员包含在内。

class myInterface
{
    virtual void foo() = 0;
}

class iHaveAnInterface: public myInterface
{
    void foo();
}

void iHaveAnInterface::foo()
{
    //implement
}
于 2012-08-14T10:23:09.580 回答
0

C++ 中的接口最好写成抽象基类:包含至少一个纯虚函数的无状态类。具体类从接口类公开派生,并(可选)添加状态以实现接口。

class IFoo
{
public:
    // enable deletion of implementation classes through IFoo*
    virtual ~IFoo() {}

    // interface
    void bar() 
    { 
        // add optional pre-processing here
        do_bar(); 
        // add optional post-processing here
    }

private:
    // to be overridden by implementation classes
    virtual void do_bar() = 0; // pure virtual

    // no state here!
};

class FooImpl
:
    public IFoo
{
public:
    // constructors, assignment etc. to manipulate state

private:
    virtual void do_bar() 
    { 
         // your implementation 
    }

    // add your state here
};

void my_algorithm(IFoo* foo)
{
    foo->bar();
}

int main()
{
    IFoo* f = new FooImpl();
    my_algorithm(f); // resolves to using FooImpl::do_bar()

    return 0;
}

上面的代码是在运行时多态性和显式接口方面的。在 C++ 中有一个使用编译时多态性和隐式接口的替代方案。这是使用模板和 Curiously Recurring Template Pattern 完成的。

请注意,我在这里使用了非虚拟接口(NVI)成语:公共接口IFoo是非虚拟的,实现是(纯)虚拟的。这为接口的编写者提供了更大的灵活性,例如在IFoo::bar()客户不注意的情况下添加日志记录和其他检查等。

于 2012-08-14T10:40:11.257 回答