我最近在和一个学生一起工作时遇到了一个场景,我在努力理解以下示例失败的原因。
我有一个指向对象Game
的指针,而Game
本身有一个指向vector<Pair>
的指针。失败的行是main()
的最后一行,在这里我是菊花链方法:
gamePointer->getPairs()->push_back(pair);
在上面的行中,getPairs()
返回一个vector<Pair>*
,然后调用push_back()
将一个新的Pair
添加到向量中。结果为read access violation
。有趣的是,将Game
的{{1}}换成vector<Pair>
,使我可以编写以下内容,并且可以正常工作:
string
我已经简化了问题并复制了完整的示例:
gamePointer->getPairs()->append("B");
答案 0 :(得分:6)
Game::Game()
{
vector<Pair> pairs; // DANGER!
pairs.reserve(10);
this->setPairs(&pairs); // ARGHH!
} // < pairs dies on this line
名为vector
的{{1}}仅在构造函数运行时有效。您存储了指向它的指针,但是指向对象的对象立即超出范围!
相反,只需将成员设为pairs
而不是指针:
vector
然后您可以像这样制作class Game
{
private:
vector<Pair> pairs; // the vector itself is a member of Game
:
getPairs
或者这个:
vector<Pair>* Game::getPairs() // return a pointer
{
return &pairs;
}
您当前正在做的是Undefined Behaviour-这意味着您的程序是非法的,并且可能会发生任何事情,包括看起来正常工作。
当您将vector<Pair>& Game::getPairs() // return a reference
{
return pairs;
}
换成vector
时,会看到“看起来正常工作”的内容-代码仍然损坏,只是您没有注意到!
我可以对发生这种情况的原因进行有根据的猜测,但这绝不能保证。
string
行为:
vector
对象本身位于堆栈上,但必须使用vector
在堆上分配一个缓冲区。new
在delete
末尾超出范围时,它vector
缓冲此缓冲区。Game::Game()
对象本身不再有效,但是在您下次尝试使用它之前,存储器恰好不会被覆盖。vector
,并且内存仍然碰巧包含指向缓冲区的指针。缓冲区已释放,因此尝试访问它时会遇到“读取访问冲突”。 vector
行为:
string
没有没有分配缓冲区的权限。这是string
的有效实现,它可以使用“小型字符串优化”,其中小字符串(例如,最多16个字符)直接存储在std::string
对象本身内部,而不是在分配的缓冲区中。string
(包括实际内容)在堆栈上。string
对象在string
的末尾超出范围,但是在您下次尝试使用它之前,存储器恰好不会被覆盖。Game::Game()
,并且内存中仍然碰巧包含有效的“短字符串”魔术。答案 1 :(得分:3)
看Game
的构造函数:
Game::Game()
{
vector<Pair> pairs;
pairs.reserve(10);
this->setPairs(&pairs);
}
pairs
是局部变量。它将在构造函数的末尾销毁。含义this->pairs
是dangling pointer。可以通过直接在构造函数中直接分配this->pairs
来解决此问题:
Game::Game()
{
this->pairs = new vector<Pair>;
this->pairs->reserve(10);
}
如果这样做,请始终提供一个解构函数来清理分配的数据:
Game::~Game()
{
delete this->pairs;
}
这使您可以摆脱setPairs
。最后,该类应如下所示:
class Game
{
private:
vector<Pair>* pairs;
public:
Game();
~Game();
vector<Pair>* getPairs();
};
说实话,我真的不喜欢您混合vector
之类的类和原始指针的方式。在这个简单的示例中,您甚至根本不需要指针。有关如何制作无指针的实现,请参见BoBTFish answer。但是,如果绝对有义务使用指针,请改用std::unique_ptr<std::vector<Pair>>
或std::shared_ptr<std::vector<Pair>>
。还请考虑摆脱using namespace std;
<>。