c ++:通过基类接口构造派生对象

时间:2013-10-20 18:57:22

标签: c++ templates inheritance static-cast

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

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类中创建一个函数,它将以与derived<D>相同的方式构造construct_impl的对象,即不重新分配内存。 我在想这样的事情

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<D>*的转换,所以很遗憾无法编译(除非我使用static_cast)。有没有办法达到我的需要?提前谢谢!

2 个答案:

答案 0 :(得分: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。我们可能想要摆脱所有这些*,并且还要使每辆车“取得其内部汽车的所有权”(并且也负责删除它)。我们可以使用标准库的std::unique_ptr来表示“所有权”这一概念(另请参阅How do I pass a unique_ptr argument to a constructor or a function?)......但实际上,我们所拥有的只是一个单链表{ {1}} s,这是STL免费提供给我们的东西:

ClownCar

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

答案 1 :(得分:2)

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

我相信(我还没有尝试过)你应该在dynamic_cast<>的覆盖中测试getNext_noalloc()来测试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()是私有的或受保护的,则类必须是{{ 1}} S上。

使用friend应该意味着您毕竟不需要CRTP,但是您必须检查每个强制转换,以确保它转换为正确的类型。

Casting pointers from base type to child type