C++释放矢量元素占用的内存时遇到的麻烦



所以问题是当我尝试将非动态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
}

也 玩家.h:

class Player{
public:
...
~Player();
private:
...
IClass* typeOfClass = new Warrior();
};

和 ~播放器:

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

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

class Warrior {
public:
int GetMeleeAttack();
int GetRangedAttack();
int GetMagicAttack();
int AgilityAction();
int StrengthAction();
int IntelligenceAction();
void WhoAmI();
private:
};

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

std::vector<Player>playerList;

应该是

std::vector<Player*>playerList;

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

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

如果您使用的是指针向量,请记住在销毁向量时释放所有元素。

另一种方法是:

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

这将解决分配问题。

std::vector

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

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

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

解决方案

是的,这个问题可以通过存储指向Player的指针来解决,但这不必要地增加了必须在Player内存储指向IClass的指针引起的问题。

相反,你可以通过使整个问题消失

IClass* typeOfClass;

std::unique_ptr<IClass> typeOfClass;

std::shared_ptr<IClass> typeOfClass;

并使用

playerList.emplace_back();

Helper::Run.

TL;博士

std::vector做了很多复制和销毁副本的工作。为此,vector包含的对象必须符合三、五或零规则之一。

由于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实例的簿记,则需要unique_ptr

最新更新