C++ 在现代C+中,如何有效地为指向虚拟基类的指针向量分配空间+;

C++ 在现代C+中,如何有效地为指向虚拟基类的指针向量分配空间+;,c++,c++11,memory-management,C++,C++11,Memory Management,我有以下数据模型 struct Base { int x_; int y_; int z_; virtual int getId() const; virtual int getValue() const = 0; virtual Base* create() const = 0; bool operator<(const Base &other); }; struct Derived0 : public Base {

我有以下数据模型

struct Base {
    int x_;
    int y_;
    int z_;

    virtual int getId() const;
    virtual int getValue() const = 0;
    virtual Base* create() const = 0;
    bool operator<(const Base &other);
};

struct Derived0 : public Base {
    virtual Derived0* create() const { return new Derived0(); };
    virtual int getId() const;
    virtual int getValue() const;
};
//...
struct DerivedN : public Base {
    virtual DerivedN* create() const { return new DerivedN(); };
    virtual int getId() const;
    virtual int getValue() const;
};
struct Base{
int x_2;;
int y_;
int z_2;;
虚拟int getId()常量;
虚拟int getValue()常量=0;
虚拟基*create()常量=0;
bool运算符create());
//上一次呼叫占用了大部分时间
//...
b、 推回(ptr);
}
}
排序(b.begin(),b.end());
// ...
由于我需要大量的派生对象,我想知道是否可以更有效地进行初始化。在本例中,大多数时间都是通过创建单个共享指针来完成的

我尝试了一种预先分配
Base
对象数组的方法(因为所有
派生的
对象都具有相同的大小),为每个模板强制转换虚拟类型,并存储指向该数组的原始指针。毫不奇怪,这种方法的速度要快很多倍。 但是,它不干净,
向量
无法使用,内存管理有问题

有人能给我一个建议吗,如何在C++方式下有效地做这个?
  • 如果所有对象的大小相同
  • 如果大小不同

创建一个变体样式类型的橡皮擦,使所有东西看起来都像
Base

template<class T>struct tag{using type=T;};

template<class Base, class...Derived>
struct poly {
  Base* get(){
    return const_cast<Base*>( const_cast<poly const*>( this )->get() );
  }
  Base const* get()const{
    if (!ops) return nullptr;
    return ops->to_base(&raw);
  }
  Base* operator->(){ return get(); }
  Base const* operator->()const{ return get(); }
  Base& operator*(){ return *get(); }
  Base const& operator*()const{ return *get(); }
  explicit operator bool()const{ return get(); }

  template<class T,class...Args,
    class=std::enable_if<
    /* T is one of Derived... */
    >
  >
  void emplace(tag<T>,Args&&...args){
    cleanup();
    ops=&ops_for<T>();
    new(&raw)T(std::forward<Args>(args)...);
  }        
  poly& operator=(poly const& o){
    if (this==&o)return *this;
    cleanup();
    if (!o->ops) return *this;
    o->ops.copy_ctor( &raw, &o.raw );
    ops=o->ops;
    return *this;
  }
  poly& operator=(poly&&o){
    if (this==&o)return *this;
    cleanup();
    if (!o->ops) return *this;
    o->ops.move_ctor( &raw, &o.raw );
    ops=o->ops;
    return *this;
  }

  poly(poly const& o){
    if (!o->ops)return;
    o->ops.copy_ctor(&raw,&o.raw);
    ops=o->ops;
  }
  poly(poly&& o){
    if (!o->ops)return;
    o->ops.move_ctor(&raw,&o.raw);
    ops=o->ops;
  }

private:
  void cleanup(){
    if (ops) ops->dtor(&raw);
    ops=nullptr;
  }
  struct erase_ops{
    void(*copy_ctor)(void*lhs,void const*rhs);
    void(*move_ctor)(void*lhs,void*rhs);
    void(*dtor)(void*ptr);
    Base const*(*to_base)(void const*ptr);
  };
  template<class D>
  static erase_ops const& ops_for(){
    static erase_ops r={
      // ...
    };
    return r;
  };
  erase_ops const* ops=nullptr; // = &ops_for<Derived1>(); etc
  std::aligned_storage< /* size and alignment info */ > raw;
};
templatestruct标记{using type=T;};
模板
结构多边形{
Base*get(){
返回const_cast(const_cast(this)->get());
}
基本常量*get()常量{
如果(!ops)返回空ptr;
将ops->返回到基础(&raw);
}
Base*运算符->(){return get();}
基本常量*运算符->()常量{return get();}
基运算符*(){return*get();}
基常量&运算符*()常量{return*get();}
显式运算符bool()const{return get();}
模板
>
空位置(标记、参数和参数){
清理();
ops=&ops_for();
新(原始)T(标准::正向(参数)…);
}        
多边形和运算符=(多边形常量和o){
如果(this==&o)返回*this;
清理();
如果(!o->ops)返回*this;
o->ops.copy\u-ctor(&raw,&o.raw);
ops=o->ops;
归还*这个;
}
多边形和运算符=(多边形和o){
如果(this==&o)返回*this;
清理();
如果(!o->ops)返回*this;
o->ops.move_-ctor(&raw,&o.raw);
ops=o->ops;
归还*这个;
}
多边形(多边形常量和o){
如果(!o->ops)返回;
o->ops.copy\u-ctor(&raw,&o.raw);
ops=o->ops;
}
多边形(多边形和o形){
如果(!o->ops)返回;
o->ops.move_-ctor(&raw,&o.raw);
ops=o->ops;
}
私人:
空洞清理(){
if(ops)ops->dtor(&raw);
ops=nullptr;
}
结构擦除操作{
作废(*副本)(作废*左侧、作废常量*右侧);
无效(*移动系数)(无效*左侧,无效*右侧);
无效(*dtor)(无效*ptr);
基准常数*(*至_基准)(无效常数*ptr);
};
模板
针对()的静态擦除操作常量和操作{
静态擦除操作={
// ...
};
返回r;
};
擦除操作常量*ops=nullptr;//=&ops\u for();等
标准::校准存储原始;
};
执行遗漏,我正在打电话


一旦你有了以上内容,你就可以创建一个
poly向量。在我看来,通过使用
std::unique_ptr
并提前保留一些
std::vector
内存,你的许多性能问题都可以得到解决

std::shared_ptr<Base> ptr(templ[i]->create());
当您执行上述足够的次数时,向量将耗尽为您分配的内存,并尝试分配更多内存
std::vector
的设计方式是,这已经摊销了恒定的时间复杂性,但我们可以采取任何措施来缓解这种情况,特别是对于大型向量,都将节省时间

您的新代码可能如下所示:

std::vector<std::unique_ptr<Base>> b;
b.reserve(n * /*number of iterations*/);

for (int i = 0; i < n; i++) {
    while (...) { // hundreds of thousands iterations
        std::unique_ptr<Base> ptr(templ[i]->create());
        //...
        b.push_back(ptr);
    }
}
std::向量b;
b、 保留(n*/*迭代次数*/);
对于(int i=0;icreate());
//...
b、 推回(ptr);
}
}
另一方面,您可以通过执行以下操作来限制原型阵列创建的代码重复:

template <class Base, class... Derived, std::size_t... Idx>
auto arrayOfUniqueDerived (std::index_sequence<Idx...>)
{
    std::array<std::unique_ptr<Base>, sizeof...(Derived)> arr;
    (void) std::initializer_list<int> { (arr[Idx] = std::make_unique<Derived>(), 0)... };
    return arr;
}

template <class Base, class... Derived>
auto arrayOfUniqueDerived ()
{
    return arrayOfUniqueDerived<Base,Derived...>(std::index_sequence_for<Derived...>{});
}
模板
自动阵列函数驱动(标准::索引\u序列)
{
std::阵列arr;
(void)std::initializer_list{(arr[Idx]=std::make_unique(),0);
返回arr;
}
模板
自动阵列功能驱动()
{
返回ArrayFuniqueDrived(std::index_sequence_for{});
}
然后像这样使用它:

std::array<std::unique_ptr<Base>,3> templ =
      arrayOfUniqueDerived<Base,Derived0,Derived1,Derived2>();
std::数组模板=
ArrayOfunique驱动();

我在这里看不到派生,如何管理
int create(){new DerivedX();}
:返回类型与返回值不对应。代码被简化为无意义:我无法从中得出结论,因为有这么多错误,我如何区分“预期功能”和“输入错误”?仅通过文本的其余部分,此时为什么要阅读代码?;)你是否需要原型数组来做其他事情,或者你只是用它来填充向量?你的代码中大约有一半的行无法编译。如果你使用C++11,你可以用一个大小来调用向量构造函数(在你的例子中是n*什么),所以它会先分配足够的内存。我会检查
unique\ptr
有什么帮助,谢谢但是,我认为,通过分配更大的
Base
块,而不是为每个块调用new,可以节省大部分时间。在这种情况下,您可以查看小对象分配器,例如来自或的分配器。
template <class Base, class... Derived, std::size_t... Idx>
auto arrayOfUniqueDerived (std::index_sequence<Idx...>)
{
    std::array<std::unique_ptr<Base>, sizeof...(Derived)> arr;
    (void) std::initializer_list<int> { (arr[Idx] = std::make_unique<Derived>(), 0)... };
    return arr;
}

template <class Base, class... Derived>
auto arrayOfUniqueDerived ()
{
    return arrayOfUniqueDerived<Base,Derived...>(std::index_sequence_for<Derived...>{});
}
std::array<std::unique_ptr<Base>,3> templ =
      arrayOfUniqueDerived<Base,Derived0,Derived1,Derived2>();