3

假设有一个向量Items

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

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

items[1]->suicide();

suicide函数是:

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

什么是items矢量大小以及它现在的排列方式?这样做可以吗

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

编辑2:感谢所有答案!惊人的。所以我最终决定并找到了从对象外部执行此操作的方法,因为这是一种不好的做法并且不必要地复杂

4

7 回答 7

6

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

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

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

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

  • 这样做的方法是什么?现代和安全的 C++ 代码中的内存管理主要是使用像 and 这样的智能指针和像 andstd::shared_ptr这样std::unique_ptr的容器来完成的。std::vectorstd::map

  • 如果该类Item是一个值类型(这意味着您可以简单地复制它并且它在虚函数方面没有多态行为),那么只需将其std::vector<Item>用于您的代码。一旦从容器中移除元素,就会自动调用析构函数。容器为您完成。

  • 如果该类Item具有多态行为并且可以用作基类,则使用std::vector<std::unique_ptr<Item>>orstd::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++ 标准库的良好文档。

于 2013-06-10T09:07:37.593 回答
5

自杀成员不会改变向量。所以向量包含一个无效指针的元素,正式地你不能用一个无效的指针做很多事情,即使复制或比较它也是未定义的行为。所以任何访问它的东西,包括矢量调整大小,都是一个 UB。

虽然任何访问如果正式 UB,但只要您不取消引用指针,您的实现很有可能不会出现奇怪的行为——进行任何访问 UB 的基本原理是在寄存器中加载无效指针的机器可以陷阱,虽然 x86 是其中的一部分,但我不知道广泛的操作系统在这种模式下工作。

于 2013-06-10T08:41:48.190 回答
1

指针将变为无效,仅此而已。你应该小心不要再delete这样做了。vector<Item*>不会自行删除元素。

于 2013-06-10T08:34:19.570 回答
1

您的suicide函数对向量没有任何作用Items,更不用说它对它一无所知了。所以从向量的角度来看:调用函数时没有任何变化,这样做是可以的。

于 2013-06-10T08:33:56.553 回答
0

哇,看来你打错字了。应该是vector<Item *> Items; 关于你的问题:

  1. 矢量Items的大小没有改变,意味着它仍然有三个指向Item对象的指针。
  2. 向量的内容没有改变:在 Items[1]->suicide() 之前,Items[0] = 0x000001,Items[1] = 0x000005,Items[2] = 0x000009 在 Items[1]->suicide() 之后,项目[0] = 0x000001,项目[1] = 0x000005,项目[2] = 0x000009
  3. 这样做绝对没问题。

另外,vector 会自动管理它的内存,当你把一些元素推入其中而容量不够时,它会重新分配一个更大的空间,但是当你弹出一些元素或擦除一些元素时,它永远不会给多余的内存到系统。

代码Items[1]->sucide()只是将指针Items[1]持有或指向的内存返回给系统,它不会对指针本身做任何事情,Items[1]仍然持有相同的值,但指向一个不安全的区域。

没想到,你已经做了一个设计模式,假设你想设计一个类,并且你只允许在堆上分配它的任何对象,你可以编写以下代码:

class MustOnHeap
{
   private:
      ~MustOnHeap() { // ...}
   public:
      void suicide() { delete this;}

};

然后,该类不能有任何在堆栈上分配的实例,因为析构函数是私有的,编译器必须在对象走出其范围时安排析构函数的调用。因此,您必须在堆上分配它们,MustOnHeap* p = new MustOnHeap;然后显式销毁它: p->suicide();

于 2013-06-10T08:54:10.227 回答
0

The vector has no idea what you're doing elsewhere in the code, so it'll keep a dangling pointer to the original Item.

"Is it OK do do that?"

After suiciding the item, you should adjust the vector manually to no longer keep that dangling pointer.

于 2013-06-10T08:36:44.617 回答
0

That's ok in case of vector of pointers as vector will not call Item's destructor. But you have to somehow know which pointers are still valid.

If you are storing Items in vector by value, calling Item's destructor is not ok. When vector will be destroyed or cleared, it will call item's destructor again, causing application crash.

于 2013-06-10T08:38:41.683 回答