【问题标题】:C++ - Freeing memory allocated in main but pointed to in a seperate classC++ - 释放在 main 中分配但在单独的类中指向的内存
【发布时间】:2012-07-12 01:57:25
【问题描述】:

我想知道,如果我在 main 中使用 new 关键字创建一个对象,然后将该对象(指针)设置为由另一个类的成员指向,将原始指针设置为 null,我将在哪里删除分配的记忆?我可以在第二个类的析构函数中做到这一点,但如果该对象不是动态分配的呢?

一些代码作为例子:

World world;
Player* newPlayer = new Player("Ted");
world.setPlayer(newPlayer);
newPlayer = 0;

所以现在 World 中的 'player' 成员变量指向由 newPlayer 分配的内存,而 newPlayer 指向 null。使用完 World 对象后,我应该如何释放此内存?

【问题讨论】:

    标签: c++ memory-management


    【解决方案1】:

    这种复杂性是您应该尽量避免在 C++ 中使用原始指针的原因。 Smart pointers 的存在就是为了解决此类问题,让您免去手动跟踪谁拥有对象以及何时删除它的麻烦。

    例如:

    typedef boost::shared_ptr<Player> sp_Player;
    
    struct World {
        sp_Player player;
        World(sp_Player p) : player(p) {}
    };
    
    
    sp_Player newPlayer = new Player("Ted");
    
    World world;
    world.setPlayer(newPlayer);
    
    // Object is now automatically deleted at the correct time!
    

    【讨论】:

    • 在不知道他的对象的确切语义的情况下,很难说,但名字暗示了一个游戏,玩家是游戏中的玩家。在这种情况下,通常的智能指针可能是一个糟糕的选择。玩家的生命周期在逻辑上由游戏决定,而不是由某些代码是否碰巧持有指向它的指针。在这种情况下,使用boost::shared_ptr 是一个严重的设计错误,这将导致无穷无尽的问题。
    • @JamesKanze:如果对象是由World 创建的,那么我同意,因为遵循三规则相当容易。但是,如果对象构造发生在 World 类的外部(正如 OP 所暗示的那样),那么还有什么选择呢?一个复杂的析构函数/复制构造函数系统,跟踪对象是否被动态分配,并有条件地调用delete?
    • 我喜欢在World 中使用工厂函数来创建对象的想法,因为这允许在创建时定义它们与世界的关系。但我不赞成可能在堆栈上分配的问题:从示例中的名称来看,Player 被设计为动态分配,因此不应在堆栈上分配。 (如果这真的令人担心,解决方案是将析构函数声明为私有,并提供一个执行删除操作的“杀手”成员函数。)
    【解决方案2】:

    如其他答案所述,使用智能指针更容易,但在这种情况下,您可以将删除放在其他地方。

    如果所有玩家都传递给World,那么World 可以处理内存 - 每次World 被破坏或设置不同的玩家时,World 持有的Player 必须被删除。此外,任何玩家都不应该在其他任何地方被摧毁,否则它可能会被摧毁两次。但是在这种情况下,我会让所有 Player 都由 World 中的一个方法构造,以明确谁拥有内存管理并保持同一个类创建和销毁 Player 对象

    一些非常不完整的代码是(注意智能指针代码少得多,我认为涵盖了所有情况 - 这没有)

    class World
    {
       Player *p;
     ...
    }
    
    World() : p(nullptr) {
      ...
    }
    
    ~World() {
        delete p;
    }
    
    void World setPlayer( Palyer *aP ) {
        if ( p != aP) {
             delete p;
        }
        p = aP;
        ....
    }
    
    Player* World::createPlayer(std::string const& name){ 
      return new Player(name); 
    } 
    

    注意= Player(name) 在堆栈上创建了一个 Playeobject,并且它不在您的代码中由 new 和 delete 管理 - 这可能是这里最好的解决方案。

    还需要处理 operator= 和复制构造函数(对于后者最好阻止它被调用,否则它将需要复制 Player 对象,因此删除原始世界和副本不会尝试删除相同的播放器)

    【讨论】:

    • 类似“ void World::createPlayer(char* name){ player = Player(name); }” ?
    • +1 用于在 World 中建议工厂功能。这是一个非常好的主意,可以明确谁在主持节目。
    【解决方案3】:

    没有绝对的答案。问题是:你为什么要分配 对象动态开始?一旦你回答了这个问题,那就是 通常很明显应该删除它。

    例如,根据您的对象名称,我猜world 拥有 一切;它决定了玩家的寿命等。其中 情况下,应该对delete负责。至于可能性 在堆栈上分配Person:不要那样做。鉴于 Person 可能有一个明确的生命周期,对应 到函数中的一个块(否则,为什么要动态分配它 开头),声明它的局部变量是没有意义的 输入。

    【讨论】:

    • 我认为这是最好的总结。 Oli 和我的回答给出了特定的解决方案,但您需要根据您的设计确定最佳解决方案。
    • 我实际上并没有打算在代码中动态分配播放器,我只是想知道如果我这样做会发生什么 =)
    【解决方案4】:

    您只需要在完成后调用 delete。为了避免这种情况,请考虑使用共享指针,因为它们可以让您不再需要这样做。

    编辑:要回答您的评论,您拨打delete 的位置取决于您的应用程序,但请确保您

    1. 在分配之前不要delete它。
    2. 不要delete它,直到你确定没有其他东西需要访问它。

    一个可能的位置是在 World 对象的析构函数中,但如果不知道应用程序其余部分的详细信息,就不可能说出来。这就是为什么boost::shared_ptr 更可取的原因。

    【讨论】:

    • 我应该在哪里调用删除?当然,如果我之后直接调用它,它会删除 World 的玩家成员现在指向的内存,我最终会得到一个悬空指针?我知道我应该使用智能/共享指针,但目前我正在尝试学习 C++ 中内存管理的所有来龙去脉......虽然很难 =P 感谢您的建议
    • 在堆栈上创建对象并复制数据而不是使用指针,如果对象足够小,则 C++ 范围规则将销毁它们
    • 投反对票?我会很高兴地承认这不是最好的答案(尽管被接受了),但我是否真的说过任何明显错误的东西?如果是这样,我想知道 - 我只是一名中级编码员,想知道我的知识是否存在问题。
    【解决方案5】:

    new 应该始终跟在 delete 之后。如果您将指针设置为指向其他对象而不释放内存,则会导致dangling pointer

    【讨论】:

    • 可以,但不一定在同一个区块
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多