本文介绍了如何有效地为现代C ++中的虚拟基类指针的向量分配空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我有以下数据模型 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; }; 并按以下方式填写(简化) int n = 0; std :: shared_ptr< Base> templ [100]; templ [n ++] = std :: make_shared< Derived0>(); // ... templ [n ++] = std :: make_shared< DerivedN>(); std :: vector< std :: shared_ptr< Base>> b; for(int i = 0; i while(...){//数十万次迭代 std :: shared_ptr< Base> ; ptr(templ [i] - > create()); //以前的调用消耗大部分时间 // ... b.push_back(ptr); } } std :: sort(b.begin(),b.end()); // ... 由于我需要大量的派生对象,我想知道可以更有效地进行初始化。在显示的情况下,大部分时间是通过创建单个共享指针花费。 我尝试了一种预分配 Base 对象(因为 Derived 具有相同的大小),为每个模板强制转换虚拟类型,并存储这个数组的原始指针。毫不奇怪,这种方法快了很多倍。 然而不清洁,向量不能使用,内存管理有问题。 如果所有对象都具有相同的大小, 创建一个变体样式类型的橡皮擦,使一切看起来都不错如 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 } 基本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(); } 显式运算符bool()const {return get(); } template< class T,class ... Args, class = std :: enable_if< / * T是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< / *大小和比对信息* />生的; }; 执行省略,在手机上。 一旦你有了上面的内容,你可以创建一个 poly< Base,Derived1,Derived2,.... 的向量。成本是每个实例一个额外的指针。 现在,我们已经复制了大部分虚拟分派,所以我们可以在类型中包含其余操作 DerivedN 作为虚拟方法实现,并削减另一个指针的成本。如果 Base 稍微大点,我就不会打扰。 C ++喜欢值类型。给它想要的东西。 I have following data modelstruct 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;};and fill it in following way (simplified)int n = 0;std::shared_ptr<Base> templ[100];templ[n++] = std::make_shared<Derived0>();//...templ[n++] = std::make_shared<DerivedN>();std::vector<std::shared_ptr<Base>> b;for (int i = 0; i < n; i++) { while (...) { // hundreds of thousands iterations std::shared_ptr<Base> ptr(templ[i]->create()); // previous call consumes most of the time //... b.push_back(ptr); }}std::sort(b.begin(), b.end());// ...Since I need huge amount of derived objects I wonder if the initialization can be done more efficiently. In showed case most of the time is spend by creating single shared pointers.I tried a way with preallocating an array of Base objects (since all Derived have same size), casting virtual type for each template and storing raw pointers to this array. Not surprisingly such approach is many times faster.However is not clean, vector cannot be used and memory management is problematic.Can somebody give me an advice, how to do this efficiently in C++ wayif all objects have same size?if the size varies? 解决方案 Create a variant style type eraser that makes everything look like 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;};implementation left out, am on phone.Once you have above, you can create a vector of poly<Base, Derived1, Derived2, ..... The cost is one extra pointer per instance.Now at this point we have already duplicayed most of virtual dispatch, so we could just include in the type erase the remaining operations on DerivedN that are implemented as virtual methods and shave off another pointer's cost. If Base is modestly bigger, I would not bother.C++ loves value types. Give it what it wants. 这篇关于如何有效地为现代C ++中的虚拟基类指针的向量分配空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-30 09:06