1

这是我的问题,说我有一个函数,它由以下内容组成:

void function() {  
    entity e; //entity is just a class  
    entities.push_back(e); //entities is a vector of entity objects  
}

这是困扰我的事情。'e' 包含指向另一个对象的指针。当实体的析构函数被调用时,它会删除该指针。由于'e'和实体中的实体都指向同一个地方,如果我从实体对那个指针做一些事情,(在函数之外)它会给出一个错误,因为指针上的任何东西都被删除了函数回来。解决此问题的最佳方法是什么?

4

4 回答 4

5

C++ 是一种“基于副本”的语言,例如,一个容器Entity确实会将您提供给它的实体的副本放入容器中。

在许多地方都使用 C++ 制作了副本,因此最好您的班级正确支持它们或完全禁止它们。

您的类包含指向其他数据的指针:当您复制该类的实例时会发生什么?如果复制指针是可以的,那么显然不能在析构函数中删除指向的对象,因为仍然存在的副本将指向已删除的对象。

有一个简单的规则有助于避免这种错误,被称为“三法则”。如果您已明确编码

  • 复制构造函数
  • 析构函数
  • 赋值运算符

在您的班级中,您很可能需要所有三个。

在这种情况下,您有一个不是默认的析构函数(因为删除了指向的对象),因此您还需要告诉在复制构造或赋值的情况下要做什么。

如果您希望该类是不可复制的,那么只需确保

struct Entity {
    Object *o;

    Entity(Object *o) : o(o) {
        ...
    }

    ~Entity() {
        delete o;
    }

private:
    // Taboo... this should just never happen!!!
    // Here is a declaration, but no implementation will be written
    Entity(const Entity& other);      // Copy constructor
    Entity& operator=(const Entity&); // Assignment
};

声明禁止的操作private将确保用户代码永远不会调用它们(这将是一个编译时错误),并且只声明它们而不提供实现将确保即使是类代码本身也不会错误地调用它们(你会得到链接时错误)。

但是,在这种特定情况下,这将禁止您的代码将Entity实例放入容器中(必须复制容器内的元素)。您可以将Entity 指针放在容器中(指针可以被复制,因此std::vector<Entity *>forentities是合法的),但您将负责处理对象的正确生命周期(谁应该调用析构函数以及何时发生?)。

另一方面,如果您有一个指向类内部数据的指针,并且您希望允许复制该类的实例,您可以:

  • 也复制指出的数据
  • 在不同实例之间共享指向的数据

对于第二种解决方案,一种常见的方法是使用“引用计数”指针,即指向的数据“知道”有多少指针正在引用它,并且仅在该计数达到 0 时才被销毁。

于 2013-09-21T17:37:49.773 回答
3

将指向共享对象的指针更改为包含 ashared_ptr将是一个简单的解决方案。

于 2013-09-21T17:27:21.780 回答
3

这是因为你违反了三原则:如果你有一个析构函数,你几乎肯定还需要一个复制构造函数和一个赋值运算符。

当然,处理指针的最佳方法是找到一种完全没有指针的方法(“零规则”):在编译器生成的析构函数、构造函数和赋值运算符会负责管理资源的情况下自动为你。

于 2013-09-21T17:28:57.203 回答
0

如果实体类有析构函数,它还应该按照三的规则定义一个复制和复制赋值运算符。

不幸的是,这可能意味着您必须复制对象中指向的项目,以便在销毁对象的副本时不会删除稍后将被其他副本删除的项目。“不幸的是”,因为创建指向项目的副本通常是不可取的;在这种情况下,您遇到了设计难题——尽管 Tom Kerr 的建议 WRTshared_ptr是一个不错的选择。

另一种解决方案是禁止复制,通过将复制构造函数设为私有,或 (C++11) 使用= delete,并entity改为 (and ) 创建一个指针数组push_back(&e)

于 2013-09-21T17:36:38.597 回答