如果std :: vector元素'自杀'会发生什么(使用delete this;)?

时间:2013-06-10 08:31:17

标签: c++ vector self-destruction

假设有一个Item s

的向量
vector<Item*> items; //{item1, item2, item3}

然后,在代码的其他部分,

items[1]->suicide();

suicide函数是:

void Item::suicide()
{
   delete this;
}

什么是items矢量大小以及现在如何安排? 这样做好吗

编辑(我可以问一个额外的问题吗?):如果输出的所需排列是{item1, item3},大小是2,并且没有悬空指针,如何是以自我毁灭的方式(来自item2本身)吗?

编辑2 :感谢您的所有答案!真棒。所以我最终决定并找到了从对象外面做的方法,因为这是一种不好的做法而且不必要的复杂

7 个答案:

答案 0 :(得分:6)

什么是项目矢量大小以及现在如何安排?相同。函数调用根本不会改变矢量内容或大小。它只是释放指针指向的内存。

这样做可以吗?更确切地说:它是合法的C ++吗?是。这是好的风格编程吗?不,让我详细说明后者:

  • 应该分开关注点:谁负责内存管理?类Item或类Item本身的容器或用户?

  • 通常,容器或用户应该这样做,因为他知道发生了什么。

  • 这样做的方法是什么?现代和安全的C ++代码中的内存管理主要使用std::shared_ptrstd::unique_ptr等智能指针以及std::vectorstd::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)

指针将变为无效,即全部。你应该小心不要再deletevector<Item*>不会自行删除元素。

答案 4 :(得分:0)

该向量不知道您在代码中的其他位置正在做什么,因此它会保留dangling pointer到原始Item

  

“这样做可以吗?”

在自杀物品后,你应该手动调整矢量,不再保留悬挂指针。

答案 5 :(得分:0)

对于指针向量的情况,这是好的,因为向量不会调用Item的析构函数。但是你必须知道哪些指针仍然有效。

如果按值存储项目,则调用Item的析构函数不正常。当vector被销毁或清除时,它会再次调用item的析构函数,导致应用程序崩溃。

答案 6 :(得分:0)

哇,好像你输错了。它应该是vector<Item *> Items; 至于你的问题:

  1. 矢量项的大小不会改变,意味着它仍然有三个指向Item对象的指针。
  2. 向量的内容不会改变: 在Items [1] - &gt; suicide()之前,Items [0] = 0x000001,Items [1] = 0x000005,Items [2] = 0x000009 在Items [1] - &gt; suicide()之后,Items [0] = 0x000001,Items [1] = 0x000005,Items [2] = 0x000009
  3. 这样做绝对可以。
  4. 此外,矢量将自动管理其内存,当你将一些元素推入其中而容量不足时,它将重新分配更大的空间,但是,当你弹出一些元素或删除一些元素时,它永远不会给系统的冗余内存。

    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();