【问题标题】:c++: Construct derived object via base class interfacec++:通过基类接口构造派生对象
【发布时间】:2013-10-20 18:57:21
【问题描述】:

我有一个模板类,它由两个参数构成,一个整数和该类的前一个实例。我希望能够将这些类的实例存储在容器中,这就是为什么我让它从基类继承(请忽略非智能指针):

class base {
  virtual base* getNext(unsigned x) = 0;
};

template <class D>
class derived :
  public base {

  /* no memory allocation here, simply changes the data in next */
  void construct_impl(unsigned x, const derived<D>& previous, derived<D>& next); 


  derived();            /* default constructor */

  derived(unsigned x, const derived<D>& previous) { /* construct from previous object */
    allocate_memory_for_this();
    construct_impl(x, previous, *this);
  }

  base* getNext(unsigned x) {
    return new derived(x, *this);
  }
};

现在我想在base 类中创建一个函数,它将以与construct_impl 相同的方式构造derived&lt;D&gt; 的对象,即无需重新分配内存。 我在想这样的事情

class base {
  virtual base* getNext(unsigned x) = 0;
  virtual void  getNext_noalloc(unsigned x, base* already_allocated_derived_object) = 0;
}

在派生类中会像这样被覆盖

void getNext_noalloc(unsigned x, base* already_allocated_derived_object) {
     construct_impl(x, *this, *already_allocated_derived_object);    
}

不幸的是,由于没有从 base*derived&lt;D&gt;* 的转换(除非我使用 static_cast),因此它无法编译。有什么办法可以达到我的需要吗?提前致谢!

【问题讨论】:

  • @DavidNehme:谢谢。但是,重要的是我将派生对象存储在同一个容器中。如果我没记错的话,使用 CRTP 会移除那个能力,不是吗?
  • allocate_memory_for_this(); 是做什么的? (当构造函数被调用时,已经有一个对象了。。)getNext 是否在单链表中创建一个节点,并且您希望能够在某个已分配的缓冲区中就地创建节点?
  • 这么多可疑的代码,我什至无法从合适的点开始它有什么问题!好吧,根本不值得赏金......
  • 由于'最低限度的理解'要求而试图关闭投票,但由于开放赏金而被拒绝:( ...

标签: c++ templates inheritance static-cast


【解决方案1】:

David Nehme 在 cmets 中链接到的 curiously recurring template pattern 可能会满足您的需求。它不应该阻止您将派生类的对象一起存储在同一个容器中。看起来您正在实现一个双向链表,并从给定的项目自动创建下一个项目。 (这将使从该元素到末尾的列表无效,除非它是尾部。)

我相信(我还没有尝试过)您应该在 getNext_noalloc() 的覆盖中测试 dynamic_cast&lt;&gt; 以测试 next 指针并调用匹配类的 construct_impl()

// override in derived class
void getNext_noalloc(unsigned x, base* already_allocated_derived_object) {
  derived<D1>* p1 = dynamic_cast< derived<D1> >(already_allocated_derived_object);
  derived<D2>* p2 = dynamic_cast< derived<D2> >(already_allocated_derived_object);  
  if(p1 != NULL) {
    p1->construct_impl(x, *this, *p1); // 2nd parameter should take base type
  } else if(p2 != NULL) {
    p2->construct_impl(x, *this, *p2); // 2nd parameter should take base type
  }
}

这确实假设两个类相互了解,因此在声明类之后您必须拥有函数定义,如果 construct_impl() 是私有或受保护的,则类必须是 friends .

使用dynamic_cast&lt;&gt;() 应该意味着您根本不需要 CRTP,但您必须检查每个强制转换以确保它转换为正确的类型。

Casting pointers from base type to child type

【讨论】:

  • 您能否举一个使用 CRTP 的示例,我可以将派生对象保存在同一个容器中?那么,如果我有derived&lt;D1&gt;derived&lt;D2&gt;,如何将这两个存储在同一个容器中?谢谢
  • 啊,好的。我明白你想要做什么。我会更新我的答案。
【解决方案2】:

您可能误以为可以用 C++ 编写代码

class ClownCar {
    unsigned int x;
    ClownCar inner_car;
};

但这是不可能的! sizeof(ClownCar) 会是什么?它必须至少是sizeof x + sizeof inner_car;即sizeof(unsigned int) + sizeof(ClownCar);即,至少比自身大四个字节。

所以,一个类不能包含它自己的类的实例。继承,无论是虚拟的还是其他的,在这里都无关紧要。那么我们该怎么办?我们使用指针!

class ClownCar {
    unsigned int x;
    ClownCar *inner_car;
public:
    ClownCar() : x(0), inner_car(nullptr) {}
    ClownCar(unsigned int x, ClownCar *previous) : x(x), inner_car(previous) {}

    ClownCar *getNext(unsigned int x) {
        return new ClownCar(x, this);
    }
};

int main() {
    ClownCar inmost_car;
    ClownCar *car1 = inmost_car.getNext(42);
    ClownCar *car2 = car1.getNext(43);
    // ...
    delete car2;
    delete car1;
    // of course we don't delete inmost_car, since it lives on the stack
}

当然,这不是很 C++ish。我们可能想摆脱所有这些*s,并让每辆车“拥有”其内部汽车的所有权(并负责删除它)。我们可以使用标准库的std::unique_ptr 来表示“所有权”的概念(另请参见How do I pass a unique_ptr argument to a constructor or a function?)...但实际上,我们在这里得到的只是ClownCars 的单链表,这就是 STL 免费提供给我们的东西:

struct ClownCar { unsigned int x; };
typedef std::list<ClownCar> ClownCarList;  // ta-da!

所以我认为真正的问题是,你想要完成什么?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-28
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    相关资源
    最近更新 更多