在std::vector对象(不是指针)上调用push_back会产生严重的副作用.所以指针会更好
Calling push_back on a std::vector of objects (not pointers) has serious side effects. So would pointers be better?
这是我上一个问题的延续,在那里我提供所有信息的速度很慢,所以我正在创建一个新的问题,希望得到更多的输入。鉴于:
struct otherClass {
ImportantObj *ptrToImpObj;
otherClass() {ptrToImpObj = NULL;}
};
struct anEntry {
Thing *thing;
std::vector<otherClass> *iDM2P2H;
anEntry(Thing *tg, std::vector<sqdDMPair> *dM2Pair = NULL)
: thing(tg), iDM2P2H(dM2Pair) {}
~anEntry() {delete iDM2P2H;}
};
std::vector<anEntry *> aQueue;
std::vector<anEntry> bQueue;
void aMethod () {
Thing thingy = &(masterArrayOfThings[42]);
aQueue.push_back(new anEntry(thingy, iDM2P2H));
}
void bMethod () {
Thing thingy = &(masterArrayOfThings[42]);
bQueue.push_back(anEntry(thingy, iDM2P2H));
}
第二个方法将在复制构造函数涉及的两个对象之间共享的内存中调用dr。
在这种情况下,我觉得我应该在vector中使用指针,aQueue比bQueue更可取。
谢谢。
_ 编辑 _
假设我有20个aQueue
,它们将被清除,iDM2P2H
将被替换,每秒数百次(数千次?),因为AI路线评估认为合适。
关于删除iDM2P2H
,无论是现在还是将来,您的程序将导致该错误。如果在两个对象中设置相同的指针,那么它们迟早都会死亡,并且它们的析构函数将尝试delete
相同的内存。如果您使用指针和new
对象,当您删除anEntry
对象时,问题仍然存在。
解决方案就是避免在anEntry
析构函数中删除iDM2P2H
,并在创建它的人的同一上下文中删除它。也就是说,如果它是在程序启动时创建的,您可以在完成对它的需要时在程序的主执行路径中删除它。
您的问题是您的anEntry
复制构造函数坏了。默认的复制构造函数(anEntry (const anEntry &)
)简单地复制所有成员;对于类的显式析构函数,这会导致双重释放。这同样适用于默认的operator=
。根据三法则,如果你定义了析构函数、复制构造函数和operator=
中的任何一个,你通常应该实现其他两个(或者通过将它们设为私有和未实现来禁止它们);否则,其中一个的默认实现很可能会导致这样的问题。
现在,vector
类需要一个工作的复制构造函数。这是vector
合同的一部分。如果你的类有一个缺失的复制构造函数(即,通过将其设置为私有而禁止),编译器将出错,防止这些"严重的副作用"。如果类有一个破碎的复制构造函数,那么,这不是vector
的错。
注意,您可能需要考虑在anEntry
类中使用raii风格的分配。例如,将iDM2P2H
改为std::vector<otherClass>
,而不是std::vector<otherClass> *
。通过这样做,您根本不需要析构函数,如果您可以接受默认的复制语义,那么在这种情况下,您可以使用默认的复制构造函数。
也就是说,vector
的复制有时确实会带来很大的开销。你可以做很多事情来解决这个问题;但是,我建议使用而不是原始的std::vector<anEntry *>
,原因是这不会自动清除指向元素。
相反,使用std::vector<std::unique_ptr<anEntry>>
(如果您有c++ 0x编译器)或boost::ptr_vector<anEntry>
。这将为您提供vector
的自动销毁好处,但不会复制任何元素(因为向量是指向对象的指针向量)。注意,在unique_ptr
的情况下,您需要使用std::move
来向向量添加元素。
或者,如果编译器支持c++ 0x移动感知容器,您可以编写一个移动构造函数:
struct anEntry {
Thing *thing;
std::vector<sqdDMPair> iDM2P2H;
anEntry(Thing *thing_, std::vector<sqdDMPair> *vec = NULL)
: thing(thing_), iDM2P2H(vec ? *vec : std::vector<sqdDMPair>())
{ }
// Default copy constructor and destructor OK
// Move constructor
anEntry(anEntry &&src)
: thing(src.thing), iDM2P2H(std::move(src.iDM2P2H)) { }
anEntry &operator=(anEntry &&other) {
if (this != &other) {
thing = other.thing;
iDM2P2H = std::move(other.iDM2P2H);
}
}
};
使用std::move
允许将iDM2P2H
的内容移动到外部向量中的新位置,而不需要递归地复制。但是,对c++ 0x的支持仍处于初级阶段,您的编译器和STL可能还不支持它(如果std::vector<std::unique_ptr<int>>
可以编译,您可能还可以)。
这取决于你的对象有多昂贵和有多大。如果对象很小,拥有一个对象向量是可以的,但是如果它们很大或者很昂贵,所有的复制/构造/析构操作加起来会导致很大的性能损失,在这种情况下,你应该使用指针(然后你必须为所有对象管理内存,等等)。
1如果一个类没有很多数据成员(所以它的大小不大),但是管理着昂贵的资源,当它被复制、销毁或创建时,这些资源会发生变化,那么这个类就会小而昂贵。
如果你真的有对象包含指向共享数据/资源的指针,也许你可以考虑使用std::shared_ptr<my_shared_type>
,而不仅仅是在dtor
中调用delete
。
使用指针的std::vector<>
可能会使问题暂时消失,但它可能只是掩盖了共享资源管理的一个更基本的问题。
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- 什么是更好的做法?通过指针或标识符传递类成员?
- 为什么要使用引用来获取 *char?当我们使用指针时,不使用引用不是更好吗?
- 需要更好地了解使用 Windows API 的智能指针
- 函数的返回是 LPVOID 或更好的指向列表的指针
- 有没有更好的方法来添加两个智能指针?
- 指针与参考示例,在什么情况下更好
- 更好的方法? 新指针,需要删除吗?
- 结构中的矢量是否耗时?使用指针更好吗?
- C++/pimpl:原始指针还是unique_ptr?什么是更好的选择?
- 更好的是:存储对象与存储指针
- 更好地解释C++指针功能
- 我们是否应该将指向类实例的智能指针存储在大型 std::vector 中以获得更好的性能?
- 执行直接指针操作还是 [] 更好
- 有没有更好的方法来删除对存储在 vector 中的元素的原始指针引用
- 对成员类/对象变量使用指针更好吗
- 在C++中使用指向数组中对象的指针更好吗
- 何时使用引用比指针更好
- 是封装UI更好,还是在QT Creator中传递一个指向UI的指针更好
- 更新值还是更改指针更好