菊花链函数调用指针

Mat*_*ths 3 c++ pointers vector

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

我有一个指向对象的指针Game,而Game它本身也有指向的指针vector<Pair>。失败的行是的最后一行main(),其中我采用菊花链方法:

gamePointer->getPairs()->push_back(pair);

在上面的行中,getPairs()返回vector<Pair>*,然后push_back()调用将一个新值添加Pair到向量中。结果是read access violation。有趣的是,换出Gamevector<Pair>一个string,比方说,让我写了以下内容,它的工作原理:

gamePointer->getPairs()->append("B");

我简化了问题,并复制了完整的示例:

#include "pch.h"
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Pair 
{
private:
    string previous;
    string next;

public:
    Pair();
    Pair(string previous, string next);

    string getPrevious();
    string getNext();

    void setPrevious(string previous);
    void setNext(string next);
};

class Game 
{
private:
    vector<Pair>* pairs;
public:
    Game();

    vector<Pair>* getPairs();
    void setPairs(vector<Pair>* pairs);
};


Pair::Pair()
{
    this->setPrevious("a");
    this->setNext("b");
}

Pair::Pair(string previous, string next)
{
    this->setPrevious(previous);
    this->setNext(next);
}

string Pair::getPrevious()
{
    return this->previous;
}

string Pair::getNext()
{
    return this->next;
}

void Pair::setPrevious(string previous)
{
    this->previous = previous;
}

void Pair::setNext(string next)
{
    this->next = next;
}

Game::Game()
{
    vector<Pair> pairs;
    pairs.reserve(10);

    this->setPairs(&pairs);
}

vector<Pair>* Game::getPairs()
{
    return this->pairs;
}

void Game::setPairs(vector<Pair>* pairs)
{
    this->pairs = pairs;
}

int main()
{
    Game game;
    Game* gamePointer = &game;

    Pair pair("Previous", "Next");
    gamePointer->getPairs()->push_back(pair);
}
Run Code Online (Sandbox Code Playgroud)

BoB*_*ish 6

Game::Game()
{
    vector<Pair> pairs; // DANGER!
    pairs.reserve(10);

    this->setPairs(&pairs); // ARGHH!
} // < pairs dies on this line
Run Code Online (Sandbox Code Playgroud)

vector命名pairs构造函数运行时只活。您存储了指向该对象的指针,但是指向对象的对象立即超出范围!

而是将成员设为a vector而不是指针:

class Game 
{
private:
    vector<Pair> pairs; // the vector itself is a member of Game
Run Code Online (Sandbox Code Playgroud)

然后您可以getPairs这样:

vector<Pair>* Game::getPairs() // return a pointer
{
    return &pairs;
}
Run Code Online (Sandbox Code Playgroud)

或这个:

vector<Pair>& Game::getPairs() // return a reference
{
    return pairs;
}
Run Code Online (Sandbox Code Playgroud)

您当前正在做的事情是未定义的行为 -这意味着您的程序是非法的,并且可能会发生任何事情,包括看起来工作正常

当您将换成vectora 时,您会看到“看起来正常工作”的内容string-您的代码仍然损坏,只是您没有注意到!


我可以对发生这种情况的原因进行有根据的猜测,但这绝不能保证。

vector 行为:

  • 所述vector对象本身就是上堆叠,但是它必须在分配缓冲区使用new
  • 然后,deletevector末尾超出范围时,将使用此缓冲区Game::Game()
  • vector对象本身不再有效,但内存恰好不会被覆盖之前,您接下来尝试使用它。
  • 您尝试使用(不再有效)vector,并且内存仍然碰巧包含指向缓冲区的指针。缓冲区已被释放,因此在尝试访问它时会遇到“读取访问冲突”。

string 行为:

  • string不会具有分配的缓冲区。它的有效实现是std::string使用“小字符串优化”,其中小字符串(例如,最多16个字符)直接存储在string对象本身内部,而不是存储在分配的缓冲区中。
  • 因此string,包括实际内容在内的都在堆栈上
  • string对象在的末尾超出范围Game::Game(),但是在您下次尝试使用它之前,内存恰好不会被覆盖。
  • 您尝试使用(不再有效)string,并且内存仍然碰巧包含有效的“短字符串”魔术。
  • 因为这是在堆栈上,而不是堆上,所以实际上尚未释放内存。所以,要访问它不会导致“读访问冲突”。
  • 虽然它仍然是完全非法的!