C ++中最接近定义类的超类的东西是什么?

时间:2017-06-02 12:58:19

标签: c++ superclass subclassing idiomatic

假设我有班级

class A {
protected:
    int x,y;
    double z,w;

public:
    void foo();
    void bar();
    void baz();
};

在我的代码和其他代码中定义和使用。现在,我想写一些可以很好地在A上运行的库,但它实际上更通用,并且可以在以下操作:

class B {
protected:
    int y;
    double z;

public:
 void bar();
};

我确实希望我的库是通用的,所以我定义了一个B类,以及它的API。

我希望能够告诉编译器 - 不是在我不再控制的A的定义中,而是在其他地方,可能在B的定义中:

  

请注意,请尝试将B视为A的超类。因此,特别是将其放在内存中,这样如果我将A*重新解释为B*,我期望B*的代码就可以正常工作。然后请实际接受A*作为B*A&作为B&等。)

在C ++中,我们可以通过另一种方式实现这一点,即如果B是我们无法控制的类,我们就可以执行一个已知类的子类" class A : public B { ... }的操作;而且我知道C ++没有相反的机制 - "通过新的B类#34;超类化已知的A类。我的问题是 - 这个机制最接近可实现的近似值是什么?

注意:

  • 这都是严格的编译时间,而不是运行时。
  • class A可能会发生 no 更改。我只能修改B的定义以及了解AB的代码。其他人仍然会使用班级A,如果我希望我的代码与他们的互动,我也会这样做。
  • 这应该最好是"可扩展的"到多个超类。所以也许我也有class C { protected: int x; double w; public: void baz(); },它的行为应该像A的超类。

5 个答案:

答案 0 :(得分:28)

您可以执行以下操作:

class C
{
  struct Interface
  {
    virtual void bar() = 0;
    virtual ~Interface(){}
  };

  template <class T>
  struct Interfacer : Interface
  {
    T t;
    Interfacer(T t):t(t){}
    void bar() { t.bar(); }
  };

  std::unique_ptr<Interface> interface;

  public:
    template <class T>
    C(const T & t): interface(new Interfacer<T>(t)){}
    void bar() { interface->bar(); }
};

我们的想法是在封面下使用类型删除(InterfaceInterfacer<T>类),以允许C采取任何可以调用的内容bar 1}}然后你的库将采用C类型的对象。

答案 1 :(得分:15)

  

我知道C ++没有相反的机制 - “超类已知   类“

哦是的确如此:

template <class Superclass>
class Class : public Superclass
{    
};

然后离开你。所有在编译时,不用说。

如果你有class A无法更改并且需要将其插入继承结构中,那么请使用

行中的内容
template<class Superclass>
class Class : public A, public Superclass
{
};

请注意,dynamic_cast将获得A*指针Superclass*指针,反之亦然。同上Class*指针。此时,您已接近 Composition Traits Concepts

答案 2 :(得分:5)

普通模板执行此操作,编译器会在您错误地使用它们时通知您。

而不是

void BConsumer1(std::vector<B*> bs)
{ std::for_each(bs.begin(), bs.end(), &B::bar); }

void BConsumer2(B& b)
{ b.bar(); }

class BSubclass : public B 
{
    double xplusz() const { return B::x + B::z; }
}
你写了

template<typename Blike>
void BConsumer1(std::vector<Blike*> bs)
{ std::for_each(bs.begin(), bs.end(), &Blike::bar); }

template<typename Blike>
void BConsumer2(Blike& b)
{ b.bar(); }

template<typename Blike>
class BSubclass : public Blike 
{
    double xplusz() const { return Blike::x + Blike::z; }
}

你使用BConsumer1&amp; BConsumer2喜欢

std::vector<A*> as = /* some As */
BConsumer1(as); // deduces to BConsumer1<A>
A a;
BConsumer2(a); // deduces to BConsumer2<A>

std::vector<B*> bs = /* some Bs */
BConsumer1(bs); // deduces to BConsumer1<B>
// etc

您可以使用BSubclass<A>BSubclass<B>作为使用B界面执行某些操作的类型。

答案 3 :(得分:4)

如果不更改类,就无法更改类的行为。在A已经定义之后,确实没有添加父类的机制。

  

我只能修改 B 的定义以及了解A和B 的代码。

您无法更改A,但可以更改使用A的代码。因此,您可以使用另一个继承自A的类(我们称之为B),而不是使用D。我认为这是所期望机制中最接近的。

D可以重用A作为子对象(可能作为基础),如果有用的话。

  

这应该最好是“可扩展”到多个超类。

D可以根据需要继承尽可能多的超类。

演示:

class D : A, public B, public C {
public:
    D(const A&);
    void foo(){A::foo();}
    void bar(){A::bar();}
    void baz(){A::baz();}
};

如果只有D继承了AA,则B的行为与C的行为完全相同。

公开继承A将允许删除所有授权样板:

class D : public A, public B, public C {
public:
    D(const A&);
};

但是,我认为有可能在使用A而不知道B的代码与使用B知识的代码之间造成混淆(因此使用{{1} })。使用D的代码可以轻松处理D,但不能反过来处理。{/ p>

根本没有继承A,而是使用会员而不允许您复制A来创建A,而是引用现有的:{/ p>

D

这显然有可能导致对象生命周期的错误。这可以通过共享指针来解决:

class D : public B, public C {
    A& a;
public:
    D(const A&);
    void foo(){a.foo();}
    void bar(){a.bar();}
    void baz(){a.baz();}
};

但是,如果其他不了解class D : public B, public C { std::shared_ptr<A> a; public: D(const std::shared_ptr<A>&); void foo(){a->foo();} void bar(){a->bar();} void baz(){a->baz();} }; B的代码也使用共享指针,那么这可能只是一个选项。

答案 4 :(得分:3)

这看起来更像静态多态而非动态。正如@ZdeněkJelínek已经提到过的,你可以使用一个模板来确保在编译时传递正确的接口。

namespace details_ {
   template<class T, class=void>
   struct has_bar : std::false_type {};

   template<class T>
   struct has_bar<T, std::void_t<decltype(std::declval<T>().bar())>> : std::true_type {};
}

template<class T>
constexpr bool has_bar = details_::has_bar<T>::value;

template<class T>
std::enable_if_t<has_bar<T>> use_bar(T *t) { t->bar(); }

template<class T>
std::enable_if_t<!has_bar<T>> use_bar(T *) {
   static_assert(false, "Cannot use bar if class does not have a bar member function");
}

这应该做你想要的(即对任何类使用bar),而不必求助于vtable查找,也没有能力修改类。应该通过设置适当的优化标志来内联这种间接级别。换句话说,你将具有直接调用bar的运行时效率。