c++ - 菊花链函数调用指针
问题描述
我最近在与学生一起工作时遇到了一个场景,我很难理解为什么下面的示例失败了。
我有一个指向对象的指针Game
,Game
它本身也有一个指向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);
}
解决方案
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
这个缓冲区。vector
Game::Game()
- 对象本身不再有效,但是在您下次尝试使用它之前,
vector
内存恰好没有被覆盖。 - 您尝试使用 (不再活着)
vector
,并且内存仍然恰好包含指向缓冲区的指针。缓冲区已被释放,因此您在尝试访问它时会遇到“读取访问冲突”。
string
行为:
string
没有分配缓冲区。使用“小字符串优化”是它的有效实现std::string
,其中小字符串(例如,最多 16 个字符)直接存储在string
对象本身内部,而不是在分配的缓冲区中。- 因此
string
,包括实际内容在内,都在堆栈中。 - 该
string
对象在 结束时超出范围Game::Game()
,但在您下次尝试使用它之前,内存恰好没有被覆盖。 - 您尝试使用 (不再活着)
string
,内存仍然恰好包含有效的“短字符串”魔法。 - 因为这是在堆栈上,而不是在堆上,所以内存实际上并没有被释放。因此,尝试访问它不会导致“读取访问冲突”。
- 但这仍然是完全非法的!
推荐阅读
- javascript - Javascript - 查找和替换特定文本
- android - 错误:程序类型已存在:com.google.gson.annotations.Expose
- mysql-8.0 - 有没有更好的批量输入方法?
- python - 如何在 Python 中按值删除多个列表项
- shell - docker run:使容器命令使用它自己的环境变量而不是主机的
- html - 如何在 Angular Material 7 中使用 CSS 操作 mat-snack-bar 模板的外观
- tensorflow - 如何在 Keras 的两个 LSTM 层之间添加注意力层
- node.js - 如何将 Heroku API(server/node-express) 连接到自定义域 gh-pages
- android - 如何修复“线程中的异常”main“java.lang.ClassNotFoundException:”错误?
- ios - 问题排序对象数组,不同的对象但相同的内容