I encountered a scenario recently whilst working with a student, and I'm struggling to understand why the following example is failing.
I have a pointer to an object Game, and Game itself has a pointer to vector<Pair>. The failing line is the last line of of main(), where I am daisy-chaining methods:
gamePointer->getPairs()->push_back(pair);
In the above line, getPairs() returns a vector<Pair>*, and then push_back() is called to add a new Pair to the vector. This results in a read access violation. Interesting, swapping out the Game's vector<Pair> with a string, say, allows me to write the following, and it works:
gamePointer->getPairs()->append("B");
I've simplified the problem and reproduced a full example:
#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
The vector named pairs only lives while the constructor is running. You store a pointer to this, but the pointed-to-object immediately goes out of scope!
Instead, just make the member a vector instead of a pointer:
class Game
{
private:
vector<Pair> pairs; // the vector itself is a member of Game
You could then make getPairs like this:
vector<Pair>* Game::getPairs() // return a pointer
{
return &pairs;
}
or this:
vector<Pair>& Game::getPairs() // return a reference
{
return pairs;
}
What you are doing currently is Undefined Behaviour - this means your program is illegal, and anything could happen, including appearing to work normally.
"Appearing to work normally" is what you are seeing when you swap the vector for a string - your code is still broken, you just don't notice!
I can make an educated guess about why that happens, but this is in no way guaranteed.
vector behaviour:
vector object itself is on the stack, but it has to allocate a buffer on the heap using new.deletes this buffer when the vector goes out of scope at the end of Game::Game().vector object itself is no longer valid, but the memory just happens to not get overwritten before you next try to use it.vector, and the memory still happens to contain a pointer to the buffer. The buffer has been released, so you get a "read access violation" when trying to access it.string behaviour:
string does not have a to allocate a buffer. It is a valid implementation of std::string for it to use a "Small String Optimisation", where small strings (let's say, up to 16 characters, for example) are stored directly inside the string object itself, rather than in an allocated buffer.string, including the actual content, are on the stack.string object goes out of scope at the end of Game::Game(), but the memory just happens to not get overwritten before you next try to use it.string, and the memory still happens to contain the valid "short string" magic.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With