假设有一个Item
s
vector<Item*> items; //{item1, item2, item3}
然后,在代码的其他部分,
items[1]->suicide();
suicide
函数是:
void Item::suicide()
{
delete this;
}
什么是items
矢量大小以及现在如何安排?
这样做好吗?
编辑(我可以问一个额外的问题吗?):如果输出的所需排列是{item1, item3}
,大小是2
,并且没有悬空指针,如何是以自我毁灭的方式(来自item2
本身)吗?
编辑2 :感谢您的所有答案!真棒。所以我最终决定并找到了从对象外面做的方法,因为这是一种不好的做法而且不必要的复杂
答案 0 :(得分:6)
什么是项目矢量大小以及现在如何安排?相同。函数调用根本不会改变矢量内容或大小。它只是释放指针指向的内存。
这样做可以吗?更确切地说:它是合法的C ++吗?是。这是好的风格编程吗?不,让我详细说明后者:
应该分开关注点:谁负责内存管理?类Item
或类Item
本身的容器或用户?
通常,容器或用户应该这样做,因为他知道发生了什么。
这样做的方法是什么?现代和安全的C ++代码中的内存管理主要使用std::shared_ptr
和std::unique_ptr
等智能指针以及std::vector
和std::map
等容器来完成。
如果类Item
是值类型(这意味着您可以简单地复制它并且在虚函数方面没有多态行为),那么只需使用std::vector<Item>
代替您码。一旦从容器中删除元素,就会自动调用析构函数。容器为你做。
如果类Item
具有多态行为并且可以用作基类,则使用std::vector<std::unique_ptr<Item>>
或std::vector<std::shrared_ptr<Item>>
代替std::unique_ptr
解决方案,因为它增加了开销。只要您停止引用某个对象,它就会被您正在使用的智能指针的析构函数自动删除。你(几乎)不再需要担心内存泄漏了。
产生内存泄漏的唯一方法是,您拥有包含std::shared_ptrs
的对象,这些对象以循环方式相互引用。使用std::unique_ptrs
可以防止此类问题。另一种出路是std::weak_ptrs
。
底线:不提供函数suicide()
。而是将责任完全交给调用代码。使用标准库容器和智能指针来管理内存。
修改关于编辑中的问题。只需写下
items.erase( items.begin() + 1 );
这适用于所有类型:std::vector
值或指针。您可以找到std::vector
和C ++标准库here的良好文档。
答案 1 :(得分:5)
自杀成员不会改变矢量。因此,向量包含一个无效指针的元素,并且形式上你无法用无效指针做很多事情,即使复制或比较它也是未定义的行为。所以访问它的任何东西,包括矢量大小调整,都是UB。
虽然正式UB有任何访问权限,但只要你不取消引用指针,你的实现很可能不会表现得很奇怪 - 进行任何访问的理由UB是加载无效指针的机器寄存器可以陷阱,而x86是其中的一部分,我不知道在这种模式下工作的广泛操作系统。
答案 2 :(得分:1)
您的suicide
函数与Items
向量的任何内容都没有关系,更不用说它对此有所了解了。所以从矢量的角度来看:当你调用函数时没有任何变化,并且可以这样做。
答案 3 :(得分:1)
指针将变为无效,即全部。你应该小心不要再delete
。 vector<Item*>
不会自行删除元素。
答案 4 :(得分:0)
答案 5 :(得分:0)
对于指针向量的情况,这是好的,因为向量不会调用Item的析构函数。但是你必须知道哪些指针仍然有效。
如果按值存储项目,则调用Item的析构函数不正常。当vector被销毁或清除时,它会再次调用item的析构函数,导致应用程序崩溃。
答案 6 :(得分:0)
vector<Item *> Items;
至于你的问题:
此外,矢量将自动管理其内存,当你将一些元素推入其中而容量不足时,它将重新分配更大的空间,但是,当你弹出一些元素或删除一些元素时,它永远不会给系统的冗余内存。
Items[1]->sucide()
的代码只是将指针Items [1]保持或指向的内存返回给系统,它对指针本身什么都不做,Items [1]仍然保持相同的值,但是指向一个不安全的地区。
出乎意料的是,您已经创建了一个设计模式,假设您想要设计一个类,并且只允许在Heap上分配它的任何对象,您可以编写以下代码:
class MustOnHeap
{
private:
~MustOnHeap() { // ...}
public:
void suicide() { delete this;}
};
然后,该类不能在堆栈上具有任何alloacated实例,因为析构函数是私有的,并且编译器必须在对象走出其范围时安排析构函数的调用。
因此,您必须在堆上MustOnHeap* p = new MustOnHeap;
分配它们,然后明确地将其销毁:p-&gt; suicide();