析构函数调用和指针

时间:2013-09-21 17:26:18

标签: c++

这是我的问题 说我有一个功能,包括这样的东西:

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

这是困扰我的事情。 'e'包含指向另一个对象的指针。当调用实体的析构函数时,它会删除该指针。由于'e'和实体中的实体都指向同一个地方,如果我对来自实体的指针执行某些操作(在函数外部)它会产生错误,因为只要函数删除指针就会被删除回。解决这个问题的最佳方法是什么?

4 个答案:

答案 0 :(得分: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 *>的{​​{1}}是合法的)但您可以负责处理对象的正确生命周期(谁应该调用析构函数以及何时应该发生?)。

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

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

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

答案 1 :(得分:3)

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:0)

如果实体类有析构函数,它还应该由rule of three定义一个复制和复制赋值运算符。

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

另一个解决方案是禁止拷贝,通过使复制构造函数为私有,或者使用= delete使(C ++ 11),并使entity成为一个指针数组(和{{1 }})。