#include "iostream"
class A {
private:
int a;
public :
A(): a(-1) {}
int getA() {
return a;
}
};
class A;
class B : public A {
private:
int b;
public:
B() : b(-1) {}
int getB() {
return b;
}
};
int main() {
std::auto_ptr<A> a = new A();
std::auto_ptr<B> b = dynamic_cast<std::auto_ptr<B> > (a);
return 0;
}
ERROR: cannot dynamic_cast `(&a)->std::auto_ptr<_Tp>::get() const
Well, std::auto_ptr<B> is not derived from std::auto_ptr<A>. But B is derived from A. The auto_ptr does not know about that (it's not that clever). Looks like you want to use a shared ownership pointer. boost::shared_ptr is ideal, it also provides a dynamic_pointer_cast:
boost::shared_ptr<A> a = new A();
boost::shared_ptr<B> b = dynamic_pointer_cast<B> (a);
For auto_ptr, such a thing can't really work. Because ownership will move to b. But if the cast fails, b can't get ownership. It's not clear what to do then to me. You would probably have to say if the cast fails, a will keep having the ownership - which sounds like it will cause serious trouble. Best start using shared_ptr. Both a and b then would point to the same object - but B as a shared_ptr<B> and a as a shared_ptr<A>
dynamic cast doesn't work that way. A : public B does not imply auto_ptr<A> : public auto_ptr<B>. This is why boost's shared_ptr provides shared_dynamic_cast. You could write an auto_ptr dynamic cast though:
template<typename R, typename T>
std::auto_ptr<R> auto_ptr_dynamic_cast(std::auto_ptr<T>& in) {
auto_ptr<R> rv;
R* p;
if( p = dynamic_cast<R*>( in.get() ) ) {
in.release();
rv = p;
}
return rv;
}
Just be aware of what happens here. Since auto_ptrs have ownership semantics, a successful downcast means the original more generally typed, auto_ptr no longer has ownership.
The reason is that auto_ptr is not actually a pointer. It's a smart pointer which is a pointer wrapper but not actually a pointer. The type that is passed as a template style argument to dynamic_cast must be a true pointer (or reference) type.
http://msdn.microsoft.com/en-us/library/cby9kycs(VS.80).aspx
You're trying to cast a A* (returned by a.get()) to std::auto_ptr<B>, and since the second is not even a pointer type this fails. Probably you just want to cast it to B*:
std::auto_ptr<A> a(new A());
std::auto_ptr<B> b(dynamic_cast<B*>(a.get()));
This will still not compile, because A and B aren't polymorphic types. A needs to have a virtual function in order to make the types polymorphic. This will compile, but the cast will just throw std::bad_cast, since it isn't really a B*.
And even if it were a B*, it will fail in horrendous ways if you try to use it. Both std::auto_ptrs a and b will assume they own the object and free it later on, resulting in all kinds of memory corruption. You probably want to use a.release() after the cast was successful.
I think c++ stores RTTI (run time type information) in the vtable. Hence to use dynamic_cast<> with an instance object, the object needs have 'vtable'. C++ creates vtable only when at least one function is declared 'virtual' in the class.
The class A and Class B there are no virtual functions. This could be reason for the dynamic_cast failure. Try declaring a virtual destructor in base class.
Related
I have 2 classes, A and B.
In class A, I have a pointer on B called Bptr.
I allocate memory for Bptr in the constructor of A, and I free memory of Bptr in A's destructor.
class B {
//whatever
public:
B(int,int);
}
class A {
private:
B * Bptr;
public:
A();
}
A::A(){
Bptr = new B(2,5);
}
A::~A(){
delete Bptr;
}
How can I integrate Boost in my code and use the smart pointer : boost::shared_ptr ? How would my code look like?
Thanks a lot!
The first question to ask yourself: why do you want to dynamically allocate the object in the first place? Can you just replace the pointer with a member of type B?
Assuming there is a good reason, then why shared_ptr (rather than scoped_ptr or, in modern C++, unique_ptr)? Why do you need shared ownership?
Once you've answered these questions, and determined that shared ownership is the right solution, just replace B* with a shared pointer, initialise it in the constructor, and get rid of the redundant destructor (assuming it's not needed for anything else).
class A {
private:
boost::shared_ptr<B> Bptr;
public:
A() : Bptr(boost::make_shared<B>(2,5)) {}
};
You could simply initialise it with a pointer, Bptr(new B(2,5)), but using make_shared makes more efficient use of memory and (in more complicated situations than this) makes it easier to ensure exception safety.
class B {
//whatever
public:
B(int,int);
}
class A {
private:
boost::shared_ptr<B> Bptr;
public:
A();
}
A::A(){
Bptr = boost::make_shared<B>(2,5);
}
A::~A(){
// Bptr automatically deleted if this is the only boost::shared_ptr pointing to it
}
Although you could simply use new B(2,5) instead of boost::make_shared<B>, the latter is exception-safe.
I have a doubt about downcasting an object in C++.
Here it comes an example:
class A { }
class B : public A {
public:
void SetVal(int i) { _v = i; }
private:
int _v;
}
A* a = new A();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
What would it happen with this example? We are modifying a base clase like if it is a child one... how does it work related with the memory?
With this cast... Is it like creating an instance of B and copying the values of A?
Thanks
A* a;
This just gives you a pointer to an A. It doesn't point anywhere in particular. It doesn't point at an A or B object at all. Whether your code works or not depends on the dynamic type of the object it is pointing at.
So there are two situations you might want to know about. First, this one:
A* a = new A();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
This will give you undefined behaviour because the dynamic_cast will return a null pointer. It returns a null pointer when the dynamic type of the object is really not a B. In this case, the object is an A. You then attempt to dereference the null pointer with b->SetVal(2), so you get undefined behaviour.
A* a = new B();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
This will work fine because the object really is a B object. The dynamic cast will succeed and the SetVal call will work just fine.
However, note that for this to work, A must be a polymorphic type. For that to be true, it must have at least one virtual member function.
That shouldn't even compile, because the classes aren't polymorphic so you can't use dynamic_cast.
If it did, it would be undefined behavior.
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A a = *(A *)new B();
a.func();
}
The question is simple: why a->func() calls function in class A even though a contains object of class B?
A a = *(A *)new B();
a.func();
Here's what happens in this code, step by step:
new B(): a new object of type B is allocated on the free store, resulting in its address
(A*): the address of the object is cast to A*, so we have a pointer of type A* actually pointing to an object of type B, which is valid. All OK.
A a: here the problems start. A new local object of type A is created on the stack and constructed using the copy constructor A::A(const A&), with the first paremeter being the object created before.
The pointer to the original object of type B is lost after this statement, resulting in a memory leak, since it was allocated on the free store with new.
a.func() - the method is called on the (local) object of class A.
If you change the code to:
A& a = *( A*) new B();
a.func();
then only one object will be constructed, its pointer will be converted to pointer of type A*, then dereferenced and a new reference will be initialized with this address. The call of the virtual function will then be dynamically resolved to B::func().
But remember, that you'd still need to free the object since it was allocated with new:
delete &a;
Which, by the way, will only be correct if A has a virtual destructor, which is required that B::~B() (which luckily is empty here, but it doesn't need to in the general case) will also be called. If A doesn't have a virtual destructor, then you'd need to free it by:
delete (B*)&a;
If you would want to use a pointer, then that's the same as with the reference. Code:
A* a = new B(); // actually you don't need an explicit cast here.
a->func();
delete (B*)a; // or just delete a; if A has a virtual destructor.
Now that you've modified your code snippet, the problem is clear. Polymorphism (i.e. virtual functions) are only invoked via pointers and references. You have neither of these. A a = XXX does not contain an object of type B, it contains an object of type A. You've "sliced away" the B-ness of the object by doing that pointer cast and dereference.
If you do A *a = new B();, then you will get the expected behaviour.
The problem you encounter is classic object slicing :
A a = *(A *)new B();
Make a either a reference or pointer to A, and virtual dispatch will work as you expect. See this other question for more explanations.
You commented on another answer that "Compiler should at least give warning or what". This is why is it considered a good practice to make base classes either abstract of non copyable : your initial code wouldn't have compiled in the first place.
This might do that trick.
A &a = *(A *)new B();
a.func();
Or
A *a = new B();
a->func();
Virtual dispatch works only with pointer or reference types:
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A* a = new B();
a->func();
}
The problem is the deference and casting of B to A with the A a = *(A *)new B();
You can fix it with just removing the *(A *) changing it to (A *a = new B(); ) but I would take it a step further since your variable name is not good for instantiation of B.
It should be
B *b = new B();
b->func();
Because you performed slicing when you copied the dynamically allocated object into object a of type A (which also gave you a memory leak).
a should be a reference (A&) instead, or just keep the pointer.
If I understand slicing correctly I don't think this could happen with pointers or smart pointers. For example, if you had:
class A
{
int something;
};
class B : public A
{
int stuff;
int morestuff;
};
int main()
{
std::shared_ptr<B> b(new B());
std::shared_ptr<A> a;
a = b;
}
My understanding is that the block of memory allocated to the object pointed to by "b" is still the same and doesn't change when assigned to the smart pointer "a".
Please confirm or reject my understanding, or let me know of any pitfalls associated with this.
A smart pointer is still a pointer, so such an assignment won't cause slicing. Slicing happens only when dealing with values, not pointers. Note, however, templates don't know about the relationships between the items the point at, so even though B derives from A, shared_pointer<B> doesn't derived from shared_pointer<A>, so an assignment doesn't (automatically) get an automatic up-cast like it would with native pointers.
Edit: elaborating on final point.
Slicing happens with values, not pointers, so (given your definitions of A and B), something like:
A ax = b;
would work, but would "slice" the B object to become an A object. If, however, you have some sort of template that holds an instance of the item:
template <class T>
class holder {
T t_;
public:
holder &operator=(T const &t) {
t_ = t;
return *this;
}
holder &operator=(holder const &t) { t_ = t; return *this; }
};
Now, if we try to assign one value to another, like would cause slicing:
holder<A> ha;
holder<B> hb;
A a;
B b;
ha = a;
hb = b;
ha = hb;
we will NOT get slicing. Instead, the compiler will simply give us an error, telling us that holder<A> and holder<B> are not related types, so the assignment can't happen -- without adding an explicit cast, it simply won't compile.
You're correct, but they're not the same: you can't evaluate a->stuff.
I have the following contrived example (coming from real code):
template <class T>
class Base {
public:
Base(int a):x(a) {}
Base(Base<T> * &other) { }
virtual ~Base() {}
private:
int x;
};
template <class T>
class Derived:public Base<T>{
public:
Derived(int x):Base<T>(x) {}
Derived(Derived<T>* &other): Base<T>(other) {}
};
int main() {
Derived<int> *x=new Derived<int>(1);
Derived<int> y(x);
}
When I try to compile this, I get:
1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27: instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error: initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’
1) Clearly gcc is being confused by the constructors. If I remove the reference
from the constructors, then the code compiles. So my assumption is that something goes wrong
with up-casting pointer references. Can someone tell me what is going on here?
2) A slightly unrelated question. If I were to do something horrendous like "delete other" in the constructor (bear with me),
what happens when someone passes me a pointer to something on the stack ?
E.g. Derived<int> x(2);
Derived<int> y(x);
where
Derived(Derived<T>*& other) { delete other;}
How can I make sure that pointer is legitimately pointing to something on the heap?
Base<T> is a base type of Derived<T>, but Base<T>* is not a base type of Derived<T>*. You can pass a derived pointer in place of a base pointer, but you can't pass a derived pointer reference in place of a base pointer reference.
The reason is that, suppose you could, and suppose the constructor of Base were to write some value into the reference:
Base(Base<T> * &other) {
Base<T> *thing = new Base<T>(12);
other = thing;
}
You've just written a pointer to something which is not a Derived<T>, into a pointer to Derived<T>. The compiler can't let this happen.
You cannot convert a reference to a pointer to Derived to a reference to a pointer to Base. (Templates don't contribute to the issue here, so removed from my example below.)
If you want to defer responsibility for a pointer, use a smart pointer type. Smart pointer types can represent the "responsibility to delete" that raw pointers cannot. Examples include std::auto_ptr and boost::shared_ptr, among many others.
Why you cannot upcast pointer references:
struct Base {};
struct Derived : Base {};
struct Subclass : Base {};
int main() {
Derived d;
Derived* p = &d;
Derived*& d_ptr = p;
Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is
Base b;
b_ptr = &b; // oops! d_ptr no longer points to a Derived!
Subclass s;
b_ptr = &s; // oops! d_ptr no longer points to a Derived!
}
When you pass your 'other' parameter to the Base ctor, you're trying to do the same thing as b_ptr = d_ptr above.
You make sure that pointer points to something on the heap by writing that in your documentation and relying on the caller to abide by that. If whoever calls your constructor passes a stack pointer, all bets are off, and it's not your fault - you can try to catch the problem early, but no guarantees.
That's how the standard library works - often it'll catch obvious errors, but it's not required to, and it's up to the caller to make sure they're not doing anything stupid.
Your x variable is not a pointer, it should be if you want to assign a new Derived<int> to it.
As for deleting things on the stack, don't do it. There is no way to tell whether you have been passed the address of something on the stack or on the heap (indeed, the C++ standard doesn't even acknowledge the existence of a stack). The lesson here is that you shouldn't be deleting things that you don't own, especially if you have no way of telling where they came from.
Not sure why do you want reference to the pointer. Why not
Base(Base<T> * other) { }
and
Derived(Derived<T>* other): Base<T>(other) {}
That should work.
And, like other answered, I don't think you can legitimately know whether the pointer is pointing into heap.
Edit: why can't one do what you're trying to: consider example:
Derived1<int> *x = new Derived1<int>
Base<int> **xx =&x;
Derived2<int> y;
*xx = &y;
Where Derived1 and Derived2 are different classes derived from Base? Would you think it's legitimate? Now that x of type Derived1* points to Derived2?