Polymorphic calls effecting local members with pointer/reference - c++

I came across a weird situation today while coding and I'm hoping someone could shed some light onto why this is happening.
I have a list of pointers to some base class:
std::list<BaseClass*> m_list;
Then I get one of the BaseClass pointers from this list
BaseClass* pBase = m_list.front();
Then I turn this base class into one of its child classes. (This is where I think the weirdness comes into play)
pBase = new ChildClass(*pBase);
The ChildClass uses the BaseClasses copy constructor to copy over all of BaseClasses fields.
Now with this ChildClass I call one of BaseClasses methods to set a field in BaseClass.
pBase->SetSomeIntMember(10);
Now if I check this int value it is 10 as expected, but it appears to only be changing it locally because if I get this same ChildClass from the list again and check it's int member it will be unchanged.
Hopefully this wasn't too tricky to follow. What makes this happen? In any situation where there is no polymorphism involved it would obviously not only be a local change as we have a pointer to the class instance. I'm guessing that I'm stomping on the pointer when I create a new ChildClass, but it definitely makes the BaseClass from the list become a ChildClass because the virtual methods still work.

pBase = new ChildClass(pBase);
This doesn't "make the BaseClass from the list become a ChildClass". It creates a new instance of ChildClass. Only changes to pBase done in ChildClass's constructor could affect what pbase pointed to before. (You cannot make one class "become an instance of a child class".)
That line of code does not change m_list at all. m_list still contains a pointer to the original BaseClass object.

You copy the value of the pointer, not a reference to the pointer.
That is,
BaseClass* pBase = m_list.front();
pBase = new ChildClass(*pBase);
is not the same as
Baseclass*& pBase_r = m_list.front();
pBase_r = new ChildClass(*pBase_r);
Remember, if you want to update the original value, you need to use references or pointers.
Note
The second example contains a memory leak since the original value of pBase is discarded before delete. To avoid such surprises, use smart pointers, e.g. std::shared_ptr<T> (C++11) or boost::shared_ptr<T> in place of T*.
Do not use std::auto_ptr<T> because its semantics are not compatible with STL containers.
So your list class should be std::list<std::shared_ptr<BaseClass>>. Another advantage here is that you can use instances of the smart pointer instead of references without messing up the internal reference count.

at first look you were just assigning the newly allocated pointer by value to pBase. The list element is actually the pointer address which got copied by value to pBase. The list element actually didn't get changed
try this instead
BaseClass** pBase = &(m_list.front());
BaseClass* pOld = *pBase;
*pBase = new ChildClass(**pBase); // you have a leak here of *pBase BTW
deleteOrCleanup(pOld); // cleanup or delete the old pBase pointer
//do what you were doing

As others have pointed out, your problem is that you're simply modifying the local copy of a pointer, and not what it's actually pointing to.
Instead of sticking raw pointers into containers and having to manually delete them (or leaking memory) when you try to replace container elements, use smart pointers.
#include <memory>
#include <list>
#include <iostream>
struct base
{
base( int a )
: x(a)
{}
int x;
};
struct derived : base
{
derived( int a )
: base(a)
{}
};
int main()
{
std::list<std::unique_ptr<base>> mylist;
mylist.push_back( std::unique_ptr<base>( new derived(10) ) );
auto pbase = mylist.front().get(); // get raw pointer to first element
std::cout << pbase->x << std::endl;
pbase = new derived( 10 * pbase->x ); // create a new derived object
mylist.front().reset( pbase ); // replace the first element, previous
// element is deleted automatically
pbase = mylist.front().get();
std::cout << pbase->x << std::endl;
// all allocated objects will be automatically deleted
// when mylist goes out of scope
}
Output:
10
100

Related

I thought pointers could only point to the same class/datatype?

I thought i knew pointers but then as i study run time polymorphism/dynamic binding, i've seen a very different use for pointers. Here are my 3 questions, they're all regarding the single line code below:
#include <iostream>
class Base {
public:
virtual void fun (int x) { std::cout << "base" << std::endl; }
};
class Derived : public Base {
public:
void fun (int x) { std::cout << "base" << std::endl; }
};
int main() {
//--------------------------------------
//1. Pointer can only hold memory address, 'Derived()' is calling the default constructor it is not a memory address
//2. What's the point of putting this into heap?
//3. I thought pointers could only hold memory address of the same datatype/class
Base* obj = new Derived();
//--------------------------------------
obj->fun(5);
return 0;
}
Pointer can only hold memory address, 'Derived()' is calling the default constructor it is not a memory address
The expression here is not Derived() (which would indeed construct a temporary object), but new Derived(), which is new's specific syntax to allocate and construct an object with dynamic lifetime and return a pointer to it. Note that the corresponding delete to end the object's lifetime is missing, but see the next point.
What's the point of putting this into heap?
None. In particular, the lifetime of an object does not affect the use of pointers with it, nor are pointers required for polymorphism (references work just as well). In any case, dynamic allocation should be done with smart pointers, not naked news.
I thought pointers could only hold memory address of the same datatype/class
That's still true: obj is pointing at the Base subobject of the Derived object. C++ provides an implicit pointer-adjusting conversion from Derived * to Base * (and Derived & to Base & as well) to facilitate the use of polymorphism.
Pointer can only hold memory address, 'Derived()' is calling the default constructor it is not a memory address
A new expression does multiple things. First, it allocates enough memory for the type of object. Then, it calls the constructor for that type at the allocated memory. Then, it returns a pointer to the newly created object. Pointers point to objects. new returns a pointer to an object. So there is no problem here, obj will now point to the newly created Derived object.
What's the point of putting this into heap?
It isn't strictly necessary to dynamically create an object here. You could just as easily have the following and illustrate polymorphism :
Derived foo;
Base* obj = &foo;
Maybe the author of this example didn't think of that, or maybe they wanted to illustrate the mechanics of new at the same time. You'd have to ask them.
I thought pointers could only hold memory address of the same datatype/class
It is true that obj is a Base* so it can only point to a Base type object. But a Derived object is also a Base object, so obj can easily point to a Derived object. That is what public inheritance achieves. The : public Base part of class Derived : public Base means Derived is also a Base.
Pointers can point to anything with a memory address.
A pointer to an instance of a derived class can be handled as if it were a pointer to the base class, even if the base class itself is abstract (unable to be instantiated itself). That's how polymorphism works in C++.
Why put a pointer to an object on the stack and the object itself into the heap? Many reasons, some of them good and others not so good.

Deleting an untyped shared_ptr

I am working on wrapping a C++ library into a C bridge.
All objects, I’d like to maintain with shared_ptrs on the heap like:
void* makeFoo() {
return new shared_ptr<void>(shared_ptr::make_shared<Foo>());
}
Can I use a generic destroy like this:
void destroy(void* p) {
delete static_cast<shared_ptr<void>*> p;
}
Or is there a cleaner way?
The type of the argument to delete must match the actual type of the thing you're deleting (otherwise how would the right destructor be invoked, for example?), or at least be a base type in a polymorphic hierarchy so the destructor can be found virtually ([expr.delete]/3).
So, no, you can't do that.
There are a two things at play here:
When you call new SomeType() ... then you need to call delete pointer where pointer has type SomeType * and points to the object allocated by that new expression. There are extensions to this rule with regards to base classes, but inheritance is not involved here, so we'll leave it at that.
shared_ptr<Foo> manages not only the Foo object, but also a "deleter" which knows how to destruct the Foo object. When you construct one shared_ptr from another, then that deleter gets passed on. This allows for "type erasure":
shared_ptr<Foo> typed = make_shared<Foo>();
shared_ptr<void> erased = typed;
Here, erased does no longer have compile time information about the type of the object it points to (that information was "erased"), but still has runtime information (the deleter) about the type of the object.
So to make this work, you need to make sure that you don't violate point 1 above; you need to delete the same type which you allocated with new: a shared_ptr<void>. This shared_ptr<void> needs to be constructed from a shared_ptr<Foo> because then it has a deleter which knows how to destruct a Foo:
void* makeFoo() {
shared_ptr<Foo> with_type = make_shared<Foo>();
shared_ptr<void> type_erased = with_type; // Just for illustration, merge with line below!
return new shared_ptr<void>(type_erased);
}
void destroy(void * ptr) {
shared_ptr<void> * original_ptr = ptr;
delete original_ptr;
}
makeFoo returns a pointer to a shared_ptr<void>. Just with the type info stripped, i.e. as void *.
destroy assumes it is passed such a pointer. Its delete calls the destructor of shared_ptr<void>. Because the shared_ptr<void> has the deleter of the original shared_ptr<Foo>, it knows how to actually destruct the object (Foo).
Side note: OP's code changed quite a few times, but still has basic syntax errors. This is not valid C++!!
delete <shared_ptr<void>*> p;
No. There is no such "generic delete".
Alternative solution: You can insert the std::shared_ptr<void> into a map, using the address of the dynamic object as key. The deallocation function can erase the shared pointer from the map.

moving smart pointers twice vs copying

Is there any significant difference in performance, memory, etc, between:
#1: moving a pointer to a temporary pointer, moving it back, then deleting the temporary pointer
#2: copying a pointer to a temporary pointer, then deleting the temporary pointer
I have the following code, two pointer of objects a Base and a Derived (which is derived from Base) are allowed to be stored inside a vector of pointers of Base objects, when reading the vector I need to check whether I need to dynamic_pointer_cast the pointer so data doesn't get sliced off.
#include "Base.h"
#include "Derived.h"
class Base
{
public:
Base() {};
~Base() {};
};
class Derived: public Base
{
public:
Derived() {};
~Derived() {};
};
int main()
{
std::vector<std::shared_ptr<Base>> vectorOfBaseObjects;
std::shared_ptr<Base> base = std::make_shared<Base>();
std::shared_ptr<Derived> derived = std::make_shared<Derived>();
vectorOfBaseObjects.push_back(base);
vectorOfBaseObjects.push_back(derived);
for (auto &it : vectorOfBaseObjects) {
// #1: Move pointer to a temporary location and move it back when done
if (std::shared_ptr<Derived> tmp_ptr = std::move(std::dynamic_pointer_cast<Derived>(it))) {
// Do something with the derived object
it = std::move(tmp_ptr);
}
// #2: Create a new temporary pointer
if (std::shared_ptr<Derived> tmp_ptr = std::dynamic_pointer_cast<Derived>(it)) {
// Do something with the derived object
}
}
}
Both statements work just fine, the only issues I could make of might be
#1: missing pointer locations in multi threaded appications in very rare cases, which could become a problem.
#2: an additional location assigned in the memory, which shoulnd't be an issue at all.
The two cases are pretty much equivalent, since std::dynamic_pointer_cast() returns a new shared pointer. it is not moved from in this expression:
std::move(std::dynamic_pointer_cast<Derived>(it))
The result of the cast is already an xvalue, so that's exactly the same as
std::dynamic_pointer_cast<Derived>(it)
The only difference is the copy of the pointer back to it. If you've not changed what it points to, then that's a wasted statement.
If you're worried about the speed of creating new shared_ptrs then tmp_ptr probably doesn't even need to be a shared_ptr. Using a raw pointer would look like this:
Derived* tmp_ptr = dynamic_cast<Derived*>(it.get());

Behavior when you set an element of a vector of classes equal to a class

If I have a class:
Class aClass
{
vector<aClass> connections;
}
with everything else declared properly, if I were to do:
aClass a = new aClass();
aClass b = new aClass();
b.connections.push_back(a);
would this create a reference to a or would it duplicate the class.
I would like it to be a pointer to the class but I wasn't sure if I needed extra syntax to ensure this. I remember reading that when a class is declared aClass a = new aClass it is creating a pointer to the object in the heap but I wasn't sure what would happen here.
For reference this is for something like a linked list.
Also if anyone can think of a better title for this question go ahead and edit it.
connections should be vector<aClass*>, aClass a should be aClass* a.
Then, for b.connections.push_back(a);, the value of the pointer will be copied, not the pointee (i.e. the object being pointed).
I'll suggest you to use smart pointers instead of raw pointers, if you do want to use pointers, like std::vector<std::shared_ptr<aClass>>.
First, vector<aClass> is not a vector of classes, it's a vector of instances of a class.
Second, aClass a = new aClass(); is hard to make to compile in C++. In most cases a default constructed instance of a class is created simply as aClass a;, and new aClass() usually returns a pointer to a fresh instance of the class.
Third, even if you push_back an instance to the vector, it is copied.
For default-constructed objects, you can construct a fresh object at the end of the vector by resizeing the vector by 1.
Since 2011, you can construct an instance via non-default constructor (with non-empty set of construction arguments) directly inside the vector using emplace_back.
If the code is made to be compilable, which I think what you intended then:
The documentation says that is has 2 overloads
void push_back( const T& value );
void push_back( T&& value );
So as long as you don't explicitly std::move or type cast or push_back return value from a function then the first overload will trigger, which means that copying push_back will be called. Otherwise the second, moving will be called.
EDIT: type cast and std::move is the same thing under the hood. But, it preserves const correctness, thus if you were to pass const T, then std::move will make it const T&&, which is very dangerous in overload resolution.
You shouldn't pass const T&& ever, about why read here.
aClass a = new aClass();
aClass b = new aClass();
b.connections.push_back(a);
It will not even compile. As someone mentioned you should write aClass *a and aClass *b.
aClass a;
aClass b;
b.connections.push_back(a);
This will copy the value to the vector, because your vector holds aClass objects (values, not addresses). It doesn't copy any address. However, if you did something like this:
vector<aClass*> connections;
aClass *a = new aClass();
aClass *b = new aClass();
b.connections.push_back(a);
Then a vector will hold a pointers to aClass objects (instances of aClass). So in this case connections[0] will point to a object. And if you do this::
cout << connections[0]<< endl;
cout << a << endl;
Then these lines are equal. They will print the same thing (same address).
Finally, if you delete that a object:
delete a;
Then it will point to that place in memory that object was placed in, but there is no a object anymore. So you can not do:
cout << a << endl;
But you still can do:
cout << connections[0] << endl;
And it will print the same address as before.

How do I delete pointer to (struct/object) without destroying pointers inside (struct/object)?

If I have a pointer to a struct/object, and that struct/object contains two other pointers to other objects and I want to delete the "object that contains the two pointers without destroying the pointers it holds" - how do I do that?
Pointer to Object A (Contains Pointer to Object B, Contains Pointer to Object C).
Delete Object A
Pointer to Object A is deleted, Pointer to Object B / C does still exists.
Is there something that I have to do to make this work?
UPDATE
It's for a game project, I hope this explains it. Right now, I have some "problems" even putting the two pointers to B, C inside the first Struct (A)
struct Player
{
char * Name;
Weapon* PlayerWeapon;
Armor* PlayerArmor;
};
struct Weapon
{
char * Name;
int Damage;
};
struct Armor
{
char * Name;
int Resistance;
};
And this somehow doesn't work.
Player* CreatePlayer(char * Name, Weapon* weapon, Armor* armor)
{
Player *pPlayer = new Player;
pPlayer->Name = name;
pPlayer->Weapon = weapon;
pPlayer->Armor = armor;
};
And later when a player "dies", the equipment should not be deleted.
The pointers (B & C) contained inside the pointer (A) will not be deleted unless you explicitly do it through your destructor. But you cant use the pointer A to access B & C once you delete the pointer A.
Note: You should be having a copy constructor and = overloaed operator in your class A to avoid shallow copying.
If you want to use the same armor and the weapon for someother player make sure you are not deleting the weapon and armor in your players destructor. Then you can use the same pointers for another player like this.
Weapon* weapon = CreateWeapon();
Armor* armor = CreateArmor();
Player* player1 = CreatePlayer("Alpha", weapon, armor);
delete player1;
Player* player2 = CreatePlayer("Beta", weapon, armor);
delete player2;
Pointer to Object A (Contains Pointer to Object B, Contains Pointer to
Object C). Delete Object A Pointer to Object A is deleted, Pointer to
Object B / C does still exists?
No, they are in undefined state. Anything can happen to them. May be in your system, you notice that they are existent, but assume it's just an illusion, which is not guaranteed every time.
As soon as, you delete A*, all it's content are available for next dynamic allocation. Possibly they might get allocated to some other object later on. Some system, may 0 out everything which is deleted. Again, anything can happen!
Is there something that I have to do to make this work?
Store B* and C* to other pointers before deleting A*.Then it's perfectly ok, because objects are intact and you have its addresses stored.
struct Foo
{
A *a;
B *b;
}
Foo *foo = new Foo();
delete foo; //foo now points to garbage. you can't use it
foo = nullptr; //don't use foo!
But you can do so:
Foo *foo new Foo();
//do some stuff with foo
A *a = foo->a;
B *b = foo->b;
delete foo;
foo = nullptr;
// if Foo does not destroy objects pointed by a,b in destructor you can still
//use them through a,b vars
a->doSomeStuff(); //ok
b->doSomeOtherStuff(); //ok
EDIT
In your case armor and weapon are not destroyed. You just lose pointers to them (and get the memeory leak). I suggest you to hold all your armor and weapons in some containers (like std::vector) to keep the pointers
I don't think this is possible without making a copy of a and b first.
You should avoid using "naked" pointers. It's better to use a smart pointer class such as shared_ptr or unique_ptr. They take care of new and delete, which are very tricky to get completely right unassisted. These are found in Boost, and shared_ptr is also in the TR1 library (std::tr1::) that probably came with your compiler, or just in plain std:: if your compiler is more recent.
Although you haven't said much about the program, if it's necessary to do something preserve the pointers, it sounds like the object A has the only copy of the pointers to B and C. That is a job for unique_ptr.
In C++11,
struct A_type {
std::unique_ptr< B_type > B; // Object *owns* B and C
std::unique_ptr< C_type > C;
};
std::unique_ptr< A_type > A( new A_type );
auto my_B = std::move( A->B ); // Acquire ownership away from object
auto my_C = std::move( A->B );
A.release(); // Delete object and anything it owns (at this point, nothing)
// my_B and my_C are guaranteed to be destroyed at some appropriate time