菊花链函数调用指针

时间:2019-03-29 12:03:30

标签: c++ pointers vector

我最近在和一个学生一起工作时遇到了一个场景,我在努力理解以下示例失败的原因。

我有一个指向对象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");

2 个答案:

答案 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上分配一个缓冲区。
  • newdelete末尾超出范围时,它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->pairsdangling 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; <>