释放向量元素占用内存的C ++问题

时间:2018-01-30 18:22:08

标签: c++ vector segmentation-fault

所以问题是,当我尝试将非动态obj推送到playerList或者当我尝试删除n时,我得到段错(核心转储)。我假设问题是在Helper类被销毁时引起的,因此向量也被破坏,所以它试图破坏自身中不再存在的对象。但是,当我使用playerList.clear()时,问题仍然存在。我想我可以使用~Helper()来破坏playerList()中的对象。但我想知道为什么我不能使用非动态对象,只是在Run()结束时将它们从playerList中清除。

class Helper{
public:

    void Run();


private:
    std::vector<Player>playerList;
    ...
};

这是Run()的样子:

using namespace std;

void Helper::Run(){
    Player *n = new Player();
    playerList.push_back(*n); //Yup. There is a memleak
}

也 Player.h:

class Player{
public:

    ...
    ~Player();

private:
    ...
    IClass* typeOfClass = new Warrior();
};

和~Player:

Player::~Player(){
    delete typeOfClass;
}

和战士(对问题没有影响)

class Warrior {
public:

    int GetMeleeAttack();
    int GetRangedAttack();
    int GetMagicAttack();
    int AgilityAction();
    int StrengthAction();
    int IntelligenceAction();
    void WhoAmI();

private:

};

Warrior的方法只返回一些整数。

3 个答案:

答案 0 :(得分:1)

std::vector<Player>playerList;

应该是

std::vector<Player*>playerList;

如果要动态分配它们。另一种方法是放置每个元素,而不是使用new。

使用new时,您在堆上进行分配,但是您通过传递堆上分配的值来在向量中创建新元素。你有一个悬空指针(内存泄漏)

如果使用指针向量,请记住在解除向量时释放所有元素。

另一种方法是:

 std::vector<std::unique_ptr<Player> >playerList;

这将解决分配问题。

答案 1 :(得分:0)

std::vector的主要工作是为您动态分配对象,因此您无需使用newdelete

void Helper::Run(){
    playerList.push_back(Player());
}

这将默认构造一个新的玩家对象并将其添加到向量中。

答案 2 :(得分:0)

解决方案

是的,可以通过存储指向Player的指针来解决问题,但这会不必要地增加由于必须在IClass内存储指针Player而导致的问题。

相反,你可以通过制作

来消除整个问题
IClass* typeOfClass;

进入

std::unique_ptr<IClass> typeOfClass;

std::shared_ptr<IClass> typeOfClass;

并使用

playerList.emplace_back();
Helper::Run中的

TL; DR

std::vector会复制和销毁副本。为此,vector包含的对象必须符合Rules of Three, Five, or Zero.

之一

由于Player拥有指针并使用默认复制功能,因此未遵守3/5/0要求的规则。因此,副本typeOfClass指向与源typeOfClass相同的位置。当源或副本被销毁时,它会删除源和副本正在使用的typeOfClass,而另一个指向无效的内存。该程序现已破裂,可能会或可能不会崩溃。

但是如果Player遵守规则并且有一个移动构造函数和一个移动赋值运算符,如下所示

class Player{
public:

    ...
    ~Player();
    Player(Player && src)
    {
        typeOfClass = src.typeOfClass;
        src.typeOfClass = nullptr;
    }
    Player& operator=(Player && src)
    {
        typeOfClass = src.typeOfClass;
        src.typeOfClass = nullptr;
    }
private:
    ...
    IClass* typeOfClass = new Warrior();
};

然后vector可以移动Players,而不会让两个Player共享相同的数据,Helper::Run可能看起来像

void Helper::Run(){
    playerList.emplace_back();
}

游戏中没有内存泄漏和更少的指针。

然而......你真正想要的是做尽可能少的特殊处理。每个特殊情况都意味着更多测试如果您可以在源头保护typeOfClass,那么Player可以作为帖子愚蠢并遵守零规则。

在这种情况下,这意味着使用像智能指针这样的代理对象来获取typeOfClass的所有权并为您管理其生命周期。如果所有勇士都有Warrior个实例,那就意味着std:shared_ptr。如果勇士需要单独的Warrior个实例来处理您想要Player的{​​{1}}个实例的记账。