0

This is probably a no-brainer, but I'm a little stumped. I've had some trouble with my vectors not behaving nicely, and now it looks like I've found the culprit. Here's a watered down version of my Player class.

class Player {
private:
    std::string _firstName;
    std::string _lastName;
public:
    Player(std::string firstName, std::string lastName) {
        _firstName = firstName;
        _lastName = lastName;
    };
    Player(const Player& otherPlayer) {
        _firstName = otherPlayer._firstName.c_str();
        _lastName = otherPlayer._lastName.c_str();
        std::cout << "Created " << _firstName << " " << _lastName << std::endl; // Why doesn't _firstName and _lastName contain anything?
    };
    std::string GetName() { return _firstName + " " + _lastName; };
};

int main(int argc, const char * argv[])
{

    Player player1 = Player("Bill", "Clinton");
    Player player2 = Player(player1);

    std::cout << "Player: " << player2.GetName() << std::endl;

    return 0;
}

The output is a meager Player:. I'm not sure why my copy constructor doesn't do what I want it to do, in particular in light of advice such as this (Zac Howland's comment accounts for the c_str();-part). Am I violating the rule of three (which, btw, I still haven't totally gotten my head around)? I'd be really grateful if anyone could point me in the right direction!

Community
  • 1
  • 1
conciliator
  • 6,078
  • 6
  • 41
  • 66
  • 1
    @Zak Howland's comments are wrong. He's describing how things used to work ~10 years ago, but (A) nowdays `std::string::operator=` is required to do a deep copy, and (B) it was always required to _act like_ a deep copy. – Mooing Duck Aug 21 '13 at 20:40
  • [I added `#include ` and `#include `](http://ideone.com/DjrNcp) – Derek Aug 21 '13 at 20:41
  • 1
    You don't actually need the copy constructor at all - the default one will do the right thing. If you do have it, you don't need the calls to `c_str()` (they just make it less robust), and you should initialise the members rather than assigning to them. But despite that I don't see any reason why it shouldn't work as it stands. What output do you get? – Alan Stokes Aug 21 '13 at 20:43
  • it (*cough*) works on my machine. – Kindread Aug 21 '13 at 20:44
  • [It works fine on ideone.](http://ideone.com/0lXVPB) – Sergey Kalinichenko Aug 21 '13 at 20:44
  • @Mooing Duck - The requirement to do a deep copy only changed with C++11. Previously, it was up to the implementer. – Zac Howland Aug 21 '13 at 21:16
  • @Derek yeah, that was just sloppy on my part. Thanks! :) – conciliator Aug 22 '13 at 09:30
  • @MooingDuck thanks for pointing out these subtleties. – conciliator Aug 22 '13 at 09:37

1 Answers1

4

It works for me : http://ideone.com/aenViu

I just addded :

#include <iostream>
#include <string>

But there is something I don't understand :

_firstName = otherPlayer._firstName.c_str();
_lastName = otherPlayer._lastName.c_str();

Why the .c_str() ? You convert the string to char* to assign it to a new string?

EDIT : From the comment, Zac Howland pointed : "Prior to C++11, if you wanted to ensure your string was copied (instead of reference counted), you had to use the c_str() method to force it to copy the string. The new standard eliminates that, but if he's using an older compiler, or one that hasn't fully implemented C++11 yet, it will ensure a deep copy."

Just do :

_firstName = otherPlayer._firstName;
_lastName = otherPlayer._lastName;

And, do you really need this copy constructor ? The default would do what you want I think...


Also, instead of assigning the members :

Player(std::string firstName, std::string lastName) {
    _firstName = firstName;
    _lastName = lastName;
}

use the member-initialization-list instead :

Player(std::string firstName, std::string lastName) :
    _firstName( std::move(firstName) ),
    _lastName( std::move(lastName) )
{}

In the first case the string's default constructor is called and then string's copy-assignment operator, there could definitely be (minor) efficiency losses compared to the second case, which directly calls the copy-constructor.

Last thing, when it is possible, does not pass values as method arguments, pass references and even const references when they don't need to be modified :

Player( const std::string& firstName, const std::string& lastName )
//      ^^^^^            ^            ^^^^^            ^
    : _firstName( firstName )  // no move here, since args are constant references
    , _lastName( lastName )
{}

Working live example of all the modifications.

Walter
  • 44,150
  • 20
  • 113
  • 196
Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62
  • 1
    Prior to C++11, if you wanted to ensure your string was copied (instead of reference counted), you had to use the c_str() method to force it to copy the string. The new standard eliminates that, but if he's using an older compiler, or one that hasn't fully implemented C++11 yet, it will ensure a deep copy. – Zac Howland Aug 21 '13 at 21:21
  • @ZacHowland Oh That's right ! I will put that in the answer ! Thank you for pointing this out ! – Pierre Fourgeaud Aug 21 '13 at 21:22
  • @PierreFourgeaud I guess, I provoked you with my deleted (I thought, it was irrelevant after I saw you use `std::move()`) comment to make an edit about using `const &`. But now, can you explain, please, the reason for `std::move()` in your constructor with `const &`? – lapk Aug 21 '13 at 21:41
  • @Petr: `std::move` is in the constructor without the `const &`. Since copies will already be made (when they are passed-by-copy), the `move` operation just allows the Player instance to take ownership of those copies (instead of creating yet another copy). – Zac Howland Aug 21 '13 at 22:23
  • @PetrBudnik Thank you for pointing this out. Was time for me to go to bed ;) Thank you Zac for editing my post ! – Pierre Fourgeaud Aug 22 '13 at 05:41
  • @ZacHowland I understand why `std::move` is in constructor with values. I was asking about one with `const &`. Which was edited. – lapk Aug 22 '13 at 08:11
  • Just to make myself clear - using `std::move` on `const &` without `const_cast` will not make compiler use move-constructor, it will still use copy-constructor (which we want). But this might be a misleading construction (to the programmer), so I thought it's better to remove it. – lapk Aug 22 '13 at 08:33
  • So I figured it out. It would've worked for me too, if I didn't link in another 'class Player' which got picked up by the compiler (which I still don't get, since the 'Player.h' was not included in my main file). Anyways: thanks to everyone, and in particular @PierreFourgeaud for a lengthy answer! :) – conciliator Aug 22 '13 at 09:44