将类转换为派生类,而不修改它

时间:2017-06-22 13:14:48

标签: c++

我正在使用一组类AB,...这些类是独立的,除了它们共有一个method。现在我想在向量中组合这些类,在一个循环中调用method。似乎最好的解决方案是从类Parent创建类派生类(见下文)。

现在问题如下。我想为每个类创建一个仅限标题的库(a.hb.h,...)。在那里,我希望课程完全独立。只有在主模块中,我才想将类“附加”到Parent,以便能够将它们组合在一个向量中。我该怎么做呢?或者我是否必须使用void*指针的向量?或者是否有另一种方法将这些类组合在一个向量中?

列表中的类:父/子范例

以下是我在向量中组合类的能力。注意我特别想避免类定义中的父/子范例。但我仍然希望将它们组合在一个载体中。

#include <iostream>
#include <vector>
#include <memory>

class Parent
{
public:
  virtual ~Parent(){};
  virtual void method(){};
};

class A : public Parent
{
public:
  A(){};
  ~A(){};
  void method(){};
};

class B : public Parent
{
public:
  B(){};
  ~B(){};
  void method(){};
};

int main()
{
  std::vector<std::unique_ptr<Parent>> vec;

  vec.push_back(std::unique_ptr<Parent>(new A));
  vec.push_back(std::unique_ptr<Parent>(new A));
  vec.push_back(std::unique_ptr<Parent>(new B));

  for ( auto &i: vec )
    i->method();

  return 0;
}

使用例如

进行编译
clang++ -std=c++14 main.cpp

3 个答案:

答案 0 :(得分:5)

基于类型擦除,静态成员函数和指向void的指针的可能解决方案,它根本不使用virtual(示例代码,远非生产就绪):

#include <iostream>
#include <vector>

struct Erased
{
    using fn_type = void(*)(void *);

    template<typename T>
    static void proto(void *ptr) {
        static_cast<T*>(ptr)->method();
    }

    fn_type method;
    void *ptr;
};

struct A
{
  void method(){ std::cout << "A" << std::endl; };
};

struct B
{
  void method(){ std::cout << "B" << std::endl; };
};

int main()
{
  std::vector<Erased> vec;

  vec.push_back(Erased{ &Erased::proto<A>, new A });
  vec.push_back(Erased{ &Erased::proto<B>, new B });

  for ( auto &erased: vec ) {
      erased.method(erased.ptr);
  }

  return 0;
}

这有助于避免使用公共基类。请在wandbox上查看。

正如评论中所述,here是一个略微修改的版本,它添加了createinvoke方法以减少用户的样板。

答案 1 :(得分:4)

这更像是伪代码,省略了细节。

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

template <class T>
struct Holder : HolderBase
{
  Holder(T* t) : t(t) {}
  T* t;
  void foo() { t->foo(); }
};

std::vector<HolderBase*> v { new Holder<A>(new A), new Holder<B>(new B) };

您还可以使用Holder的变体按值保存对象(并在同一向量中自由混合两种变体)。

如果你有一个方法可以调用,那么有一个更简单的解决方案:

A a;
B b;
std::vector<std::function<void()> v { [](){a.foo();}, [](){b.foo();} };

答案 2 :(得分:2)

你想要擦除对象的类型并统一对待它们,所以自然类型擦除是解决方案。

class with_method_t {
   struct model_t {
     virtual ~model_t() = default;
     virtual void call_method() = 0;
   };

   template<class C>
   class concept_t final : public model_t {
     C obj;
   public:
     concept_t(C const& c) : obj{c} {}
     concept_t(C&& c) : obj{std::move(c)} {}
     void call_method() override { obj.method(); }
   };

   std::unique_ptr<model_t> instance;
public:
   template<class C>
   with_method_t(C&& arg)
     : instance{std::make_unique<concept_t<C>>(std::forward<C>(arg))}
   {}

   void method() { instance->call_method(); }
};

然后给自己一个with_method_t的向量,它是一个值类型。没有原始动态分配或解除分配。该实例是通过将它接收的参数转发到一个小的多态容器中构建的:

std::vector<with_method_t> vec;
vec.emplace_back(A{});
vec.emplace_back(B{});

for ( auto &i: vec )
  i.method();