returning a local pointer

I have a question about returning a local pointer,
does the folloing code violates the local pointer rule ?
I found similar quetion, but the vector doesn't pop in that case,
so.. if the vector pop back, will it still valid in this case?
suppose, v contains v.push_back (new card())

1
2
3
4
5
6
7
8
9
10
11
12
  class Foo
{
	std::vector<card> v; // v.push_back (new card()) 
public:
	card* lastCard() {

		card* tmp = v.back(); 
		v.pop_back(); 
		return tmp; 
	}
	
};
Last edited on
If there is such a rule, then probably yes, but what you really have is a syntax error.

I hate these intermediate rules, made up by lectures to complicate matters, and never bother with them myself.

So let's fix the function in different ways to try to understand what's happening.

First. back() returns a reference, not a pointer.
https://en.cppreference.com/w/cpp/container/vector/back

Consider this version of the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class C {
    std::vector<card> v;
public:
    card* lastCard() {
        // point to the last element of v
        card* tmp = &v.back();

        // the last element of v is destroyed, the memory is still reachable, but the object will have been destroyed.
        v.pop_back();

        // return the pointer to where the object used to be, obviously you're no longer pointing to an
        // object and it's an error to use the pointer
        return tmp;
};


Version 2, use the reference.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class C {
    std::vector<card> v;
public:
    card* lastCard() {
        // refer to the last element of v
        card& tmp = v.back();

        // the last element of v is destroyed, the memory is still reachable, but the object will
        // have been destroyed.
        v.pop_back();

        // we refer to an object that is gone, we can still get the address of where we refer to,
        // but there's no object there anymore, and it's an error to try to use such an object
        return &tmp;
};


Version 3, use a value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class C {
    std::vector<card> v;
public:
    card* lastCard() {
        // take a copy of the last element of v
        card tmp = v.back();

        // the last element of v is destroyed, but we have have a copy, we're ok so far
        v.pop_back();

        // Now we have a problem. We're returning the address of an object that will be destroyed,
        // and the memory will be reused immediately, as the address is on the stack.
        return &tmp;
};


So, what's really wrong? It's a matter of design. It's probably incorrect for an object to have a method called lastCard() that deletes the last card. You'd expect it to just return the last entry. So a more appropriate version might be:
1
2
3
4
5
6
7
8
9
10
class C {
    std::vector<card> v;
public:
    const card& lastCard() const {
        return v.back();
    }
    void removeLastCard() {
        v.pop_back();
    }
};
Last edited on
Actually I wouldn't return a reference (unless you absolutely have to). If you return a reference and call removeLastCard() at the wrong time (before you are finished with the object) it is invalidated and it really hard to figure out why. So:
1
2
3
4
5
6
7
8
9
10
class C {
    std::vector<card> v;
public:
    card lastCard() const { // This way it is not a proble to call removeLastCard() at any time
        return v.back();
    }
    void removeLastCard() {
        v.pop_back();
    }
};
Topic archived. No new replies allowed.