C++ pointers and class member variables confusion

I'm wondering what is the life cycle of a class member (attribute).

Currently I have this class:

class Snake {

private:
    std::vector<Position> positions = std::vector<Position>();

public:
    void addNode(Position * position);
};

And im unsure if addNode function must receive a pointer or an object? My guessing is if addNode receives a Position position as parameter then the object will die once it is out of the scope it is created e.g

... code here ...
{
Position p = Position(..);
snake.addNode(p);
}
// p should die over here so it could cause a null pointer on the program

In the other hand, if addNode receives a pointer then p would not die unless I delete it (Hence it will not cause any nullpointer), maybe you guys can help me clarify this huge confusion about pointers I'm having right now,

Thank you.

Upvotes: 0

Views: 107

Answers (4)

Quimby
Quimby

Reputation: 19123

Class member's lifetime is tied to the instance. Its life begins in constructor's initializer list and ends at the end of the destructor call.

You seem to be under the impression that classes are reference values - using C# terminology - they are not. All C++ objects are value types:

  • {Position p;...} Is an object of type Position. It's constructor creates the object at the start of the block and its destructor is called at the end which destroys its contents and the object itself.
  • {Position* ptr{&p};...} Is an object of type Position*. It's "constructor" initializes the pointer. It's destructor is no-op but if it existed then it would simply destroy its value = address = number and not the object it points to. Pointer is the address to another object and at the end of the block the pointer is destroyed no matter it's contents. Just like {int i=0;} destroys the i, not 0.

  • {Position& ptr{p};...} same rules as pointer. The reference is destroyed, not the referenced object.

The core idea is that a pointer is just another ordinary value type:

Position p;
//Introduces a new name for a type
using Position_p = Position*;

Position_p p1 = &p;
Position_p p2 = p1;
p2++; // Change p2
//p1 is still the same.
int i1 = 5;
int i2 = i1;
i2++; // Change i2
//i1 is still the same.

They look the same and even the copy behaves as it should. Just like i2 got contents of i1, p2 gets contents of p1. For int the content is a number, for Position_p its an address. If you understand this concept then pointers should became really intuitive.

So my guess would be that you want to have std::vector<Position> and addNode(Position p). But that would incur unnecessary copies, so addnode(const Position& p) is better. Inside you can do push_back and the value is copied to the vector. If you want, you can use move semantics to get rid of this last copy.

Upvotes: 0

user11313931
user11313931

Reputation:

An object does not need to be a pointer, but a pointer is an object. A pointer just is an object that stores a number being the memory address of some object. When the pointer goes out of scope it does not delete the object it points to (hence memory leaks).

If you want your code to be the most performant and correct you should:

  1. Use a const reference instead of a pointer.
  2. Provide a move version of your function.

As follows this would be the code:

void addNode(const Position& position)
{
    positions.push_back(position);
}
void addNode(Position&& position)
{
    positions.push_back(std::move(position));
}

It allows to avoid making redundant copies and using a const Position as an argument. This way is faster than the one suggested in other answears (void addNode(Position position)) as it avoids a copy of the argument into the function.

An other optimization would be moving the position to the function when you know that it will go out of scope:

{
    Position p = Position(x, y);
    snake.addNode(std::move(p)); //mind the std::move
} //p will go out of scope here anyway, so it can safely be moved

Upvotes: 2

Let's assume that addNode has implementation

addNode(Position p){
    positions.push_back(p);
}

The input variable is a copy of the one you passed in. Once you call push_back on the vector another copy is created inside the vector and will be destroyed when the vector is destroyed. After this p will be destroyed, but this has no repercussions because vector contains a copy. Usually, if the compiler is smart, it can avoid some of the copies, and the code could be improved with the use of, for example std::move, but I do not want to add too much complexity to the answer.

Your comment about "null pointer" is a bit imprecise: if you had a vector of pointers you could insert a pointer to p in the vector. If p where to go out of scope the pointer would dangle, making the behavior of the program undefined.

Upvotes: 1

john
john

Reputation: 87959

Don't use pointers, what's wrong with the obvious?

public:
    void addNode(Position position);

The point is that although the position parameter will die once the function exits, it's value is COPIED into the vector, and the copy lives on in the vector, even when the original has died.

There really is no need for pointers here.

Upvotes: 0

Related Questions