0

I have a question regarding classes and how their members can be updated.

So basically, I have a simple class

class Player
{
public:
    Player();
    std::vector <std::string> hand = {"r4", "r1", "g5"};
};

Player::Player()
{

}

and I added instances of these classes to another vector

std::vector <Player> players;

            Player p1;
            Player p2;

            players.push_back(p1);
            players.push_back(p2);

But then I try to update the vectors of the initialized classes inside the vector storing the classes

            //doesn't work
            //p1.hand.push_back("test1");
            //p2.hand.push_back("test2");

            //works
            players[0].hand.push_back("test1");
            players[1].hand.push_back("test2");

       for (int i = 0; i < 2; i++) 
            std::cout << players[i].hand[(players[i].hand.size() - 1)] << std::endl;

I am confused why it is not adding the test strings to the vectors of the classes in the players array with the first method. Is it because it's not the same instance of the class as when I first initialized it? If someone could clarify this for me that would much appreciated. Thanks!

tothemax
  • 35
  • 6
  • Okay that makes sense, thank you. But out of curiosity is there a way I could go about putting the original in the vector? – tothemax Aug 09 '21 at 04:43
  • Change your way of writing code when dealing with C++. C++ isn't Java or JavaScript. What's the compelling reason to have the original instances? If the vector has them, the local versions will go out of scope anyway. – PaulMcKenzie Aug 09 '21 at 04:45
  • @tothemax, there's a few ways to go about it. `std::vector>` would be the "cleanest", but a vector of non-owning references is rarely the right way to go. –  Aug 09 '21 at 04:45
  • @PaulMcKenzie Yeah spot on haha, I am actually coming from only using JavaScript so I am trying to figure out the way this stuff works in C++. Do you have any recommendations of books or resources that could facilitate this transition? – tothemax Aug 09 '21 at 04:53
  • 1
    @tothemax You can start here: https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list –  Aug 09 '21 at 04:55
  • I think it's a matter of thinking about references instead of values. C++ works differently in that regard. The local versions will go out of scope and will be destroyed -- the copies in the vector remain right there (as long as the vector itself doesn't go out of scope). In addition, C++ has move semantics, so it is quite possible that any concern over efficiency when it comes to time would be a moot point. – PaulMcKenzie Aug 09 '21 at 04:57
  • Yeah I think I am slowly starting to wrap my head around this as I learn C++. I will keep this in mind as I try to rewire my brain for this language. – tothemax Aug 09 '21 at 05:11

1 Answers1

3

In C++, there is (mostly) no value types vs reference types duality. Everything is a value unless specified otherwise.

std::vector<Player> is a vector of Player instances, not a list of references to instances of Player. So when you do players.push_back(p1);, this has to make a copy of p1 so that the instance of Player in the vector belong to it.

There are ways to have vectors of references, but it's rarely the way to go. The problem with containers of non-owning references is that you must somehow be absolutely certain that every single instance referred to by the container outlives the container itself, and that can get really tricky.

In a case like yours, a more typical approach would be to have the vector be the owner of the instances. And if you absolutely must have p1 and p2, they can be variable references pointing back into it:

// Initialize the vector with 2 default-constructed players
std::vector <Player> players(2);

// ...

{
  Player& p1 = players[0];

  p1.hand.push_back("test1");
  p1.hand.push_back("test2");
  p1.hand.push_back("test3");
}

However, you need to be careful, as p1 and p2 will become invalid if you resize the vector.

  • The problem with having references to vector elements is that if the vector is resized, those references are invalidated. – PaulMcKenzie Aug 09 '21 at 04:44
  • Okay this helped clarified it a lot, appreciate the comment. Although, seeing as resizing the vector would invalidate the elements, would this be bad practice or would using a static array be more appropriate for this? – tothemax Aug 09 '21 at 04:57
  • 1
    @tothemax Moving to `std::array` would change from "you shouldn't resize" to "you can't resize". But the real answer is that local variable references should be transient. They should only exist for as short of a time as you need them, which would normally be constrained to a small block of code within which resizing doesn't happen. –  Aug 09 '21 at 05:08