用c++实现接口

本文关键字:接口 实现 c++ | 更新日期: 2023-09-27 18:19:24

我通常用c#编程,但我正在尝试做一点c++,并且正在努力尝试在c++中实现接口。

在c#中,我会这样做:
class Base<T>
{
    public void DoSomething(T value)
    {
        // Do something here
    }
}
interface IDoubleDoSomething
{
    void DoSomething(double value);
}
class Foo : Base<double>, IDoubleDoSomething
{
}
在c++中我是这样实现的:
template <class T>
class Base
{
public:
    virtual void DoSomething(T value)
    {
        // Do something here
    }
};
class IDoubleDoSomething
{
public:
    virtual void DoSomething(double value) = 0;
};
class Foo : public Base<double>, public IDoubleDoSomething
{
};

问题是我不能实例化Foo,因为它是抽象的(不实现DoSomething)。我意识到我可以实现DoSomething,只是在Base上调用方法,但我希望有更好的方法来做到这一点。我有其他类继承base与不同的数据类型,我有其他类继承从IDoubleDoSomething不使用base。

感谢您的帮助。

用c++实现接口

考虑以下c++代码

class Foo
{
public:
    void DoSomething1(){}
};
template<typename t>
void MethodExpectsDosomething1( t f )
{
    f.DoSomething1();
}
template<typename t>
void MethodExpectsDosomething2( t f )
{
    f.DoSomething2();
}
int main()
{
    Foo f;
    MethodExpectsDosomething1<Foo>( f );
    MethodExpectsDosomething2<Foo>( f );
    return 0;
}

在c++中,您可以使用Foo而不需要实现IDoSomething1IDoSomething2。第二个方法MethodExpectsDosomething2将无法编译,因为Foo没有DoSomething2方法。

在c#中这样的构造是不可能的,并且强制您拥有IDoSomething1IDoSomething2接口并将其指定为类型约束。

所以也许你需要看看你的代码,看看是否需要这样的接口

问题是,Base::DoSomethingIWhatever::DoSomething是两个不相关的函数(即使这里没有任何纯虚函数,您也无法在Foo对象上调用DoSomething)。Base需要继承IWhatever才能正常工作。

也就是说,问问你自己是否真的需要这个。在c++中,使用模板(和概念,有点像接口——参见Boost.ConceptCheck)进行泛型编程通常比运行时子类型多态性更好。

您可以将第二个模板参数传递给Base,即它要实现的接口:

template <typename T, class Interface>
class Base : public Interface { ... };
class Foo : public Base<double, IDoubleDoSomething> { ... };

对于额外的奖励点,您可以模板化IDoubleDoSomething(所以它是例如IDoSomething<double>),或者使用trait类将类型T映射到相关接口。

在c++中,纯虚函数必须总是在派生类中被重写;它们不能像那样从其他基类继承重写。如果您需要动态多态性,我认为除了在Foo中编写调用Base函数的函数之外,没有任何明智的选择。注意,Base函数不需要是虚函数。

取决于你如何使用这些类(特别是在编译时是否知道接口的每个实例的真实类型),你可以使用静态多态性将你的特定实现类作为模板参数注入到它的用户中;例如:

// No explicit interface specification with static polymorphism
class Foo : public Base<double>
{
    // Inherits DoSomething(double)
};
// Template can used with any class that impelements a 
// "DoSomething" function that can accept a double.
template <class DoubleDoSomething>
void DoSomethingWithDouble(DoubleDoSomething & interface, double value)
{
    interface.DoSomething(value);
}
// This particular specialisation uses a "Foo" object.
Foo foo;
DoSomethingWithDouble(foo, 42);

正如其他人所注意到的,基类中的两个函数是不相关的(尽管具有相同的名称和参数类型),因为它们没有公共的基类。如果你想让它们相互关联,你需要给它们一个共同的基类。

此外,通常,如果您希望多重继承正常工作,则需要将非私有基类声明为virtual。否则,如果你曾经有过公共基类(你经常需要这种风格的代码),不好的事情就会发生。

因此,您可以使您的示例工作如下:

template <class T>
class IDoSomething {
public:
    virtual void DoSomething(T value) = 0;
};
template <class T>
class Base : public virtual IDoSomething<T>
{
public:
    virtual void DoSomething(T value)
    {
        // Do something here
    }
};
class IDoubleDoSomething : public virtual IDoSomething<double>
{
};
class Foo : public virtual Base<double>, public virtual IDoubleDoSomething
{
};

如果一切正常,这应该可以工作。

class Foo : public Base<double>
{
};

在你的原始代码Foo将有2 void DoSomething(双值)方法(1是抽象的)。那将是一个Base部分和一个IDoubleDoSomething部分。Foo::Base和Foo::IDoubleDoSomething。你可以通过临时赋值和实现IDoubleDoSomething.DoSomething()来尝试。

但是因为Foo已经是一个"Base",所以没有IDoubleDoSomething你就有了你需要的东西