首页 > 解决方案 > 菊花链函数调用指针

问题描述

我最近在与学生一起工作时遇到了一个场景,我很难理解为什么下面的示例失败了。

我有一个指向对象的指针GameGame它本身也有一个指向vector<Pair>. 失败的行是 的最后一行main(),我是菊花链方法:

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

在上面的行中,getPairs()返回 a vector<Pair>*,然后push_back()调用以将新的添加Pair到向量中。这导致一个read access violation. 有趣的是,将 's 换成Game'svector<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);
}

标签: c++pointersvector

解决方案


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

    this->setPairs(&pairs); // ARGHH!
} // < pairs dies on this line

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

相反,只需使成员 avector而不是指针:

class Game 
{
private:
    vector<Pair> pairs; // the vector itself is a member of Game

然后你可以getPairs这样:

vector<Pair>* Game::getPairs() // return a pointer
{
    return &pairs;
}

或这个:

vector<Pair>& Game::getPairs() // return a reference
{
    return pairs;
}

你目前正在做的是未定义的行为——这意味着你的程序是非法的,任何事情都可能发生,包括看起来正常工作

“似乎正常工作”是您在将 a 替换vector为 a时所看到的string- 您的代码仍然损坏,只是您没有注意到!


我可以对为什么会发生这种情况做出有根据的猜测,但这绝不能保证。

vector行为:

  • vector对象本身在堆栈,但它必须使用在堆上new分配一个缓冲区。
  • 然后,当结束时超出范围时,它就是delete这个缓冲区。vectorGame::Game()
  • 对象本身不再有效,但是在您下次尝试使用它之前,vector内存恰好没有被覆盖。
  • 您尝试使用 (不再活着) vector,并且内存仍然恰好包含指向缓冲区的指针。缓冲区已被释放,因此您在尝试访问它时会遇到“读取访问冲突”。

string行为:

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

推荐阅读