Dynamic binding in C++ on copied object - c++

I have a problem in virtual function:
Here is some code as an example:
class A
{
public : virtual void print(void)
{
cout<< "A::print()"<<endl;
}
};
class B : public A
{
public : virtual void print(void)
{
cout<<"B::print()"<<endl;
}
};
class C : public A
{
public : void print(void)
{
cout<<"C::print()"<<endl;
}
};
int main(void)
{
A a,*pa,*pb,*pc;
B b;
C c;
pa=&a;
pb=&b;
pc=&c;
pa->print();
pb->print();
pc->print();
a=b;
a.print();
return 0;
}
the result:
A::print()
B::print()
C::print()
A::print()
I know it's a Polymorphism ,and know have a table called virtual-function-table,but I don't know how it is to achieve,And
a=b;
a.print();
the result is: A::print() not B::print(),why it hasn't polymorphism.
thank you!

The object a is still of type A. The assignment only copies data from b, it doesn't make a a B object.
This is called object slicing.

a=b;
a.print();
It will print A::print() because a=b causes object-slicing, which means a gets only the a-subobject of b. Read this :
What is object slicing? .
Note that runtime-polymorphism can be achieved only through pointer and reference types. In the above code, a is neither pointer, nor reference type:
A * ptr = &b; //syntax : * on LHS, & on RHS
A & ref = b; //syntax : & on LHS, that is it!
ptr->print(); //will call B::print() (which you've already seen)
ref.print(); //will call B::print() (which you've not seen yet)

Because a is not a pointer. It is an instance of A, and the assignment a=b; copies the instance of b to a. But the function call is on an instance of A.

When you do a = b; you b object is sliced i.e. only the A part of it gets copied. Polymorphism only works through pointers and references. Search "object slicing" to learn about the subject.

To learn more about virtual method table please see wiki. But in general case the table keeps the information methods' addresses. So, class A in the table will have one record says that method print is in address X. When you do pa=&b class B simply replace the table to its one in such a way that address of method print will point to address Y.
But when you do a=b you copy object. In this case polymorphism doesn't work.

After a call to any member function on a object of type A, you still have A object (except an explicit destructor call, which leaves nothing at all).
a = b;
The assignment of a class instance is just a call to a particular member function called "operator=". There is nothing special with "operator=" here, except its name is standard. You could use another name for assignment:
a = b;
// you could as well write:
a.assign(b);
// (if such member was defined)
Just as you could write add(a,b) instead of a+b, but a+b is more readable.
An explicit call to a function never changes the type of the variable it is called on:
A a;
foo(a);
// a is still a A
a.bar();
// a is still a A
The declared type of a is A, and cannot be changed to something else: this is an invariant of a.
This is also true for pointers: a variable of type pointer to A will always have the type pointer to A:
void foo (A*&);
A *bar();
A a;
A *p = &a;
foo (p); // might change p
// a still has type: pointer to A
p = bar();
// a still has type: pointer to A
But p might point to an object of type B, so, at runtime, the dynamic type of *p will be B; but the dynamic type of p is always A*.

Related

Returning owned pointers by reference

Suppose you are defining two classes, A and B, and B has a pointer to an instance of A, and is responsible for managing the lifetime of that instance. Like so:
class A;
class B
{
public:
B(A *a);
~B() { delete m_a; }
private:
A *m_a;
};
Would it be more appropriate for the method B::get_a to return a constant reference:
const A &get_a() const
{
return *m_a;
}
or just return the pointer:
const A *get_a() const
{
return m_a;
}
B has a pointer to an instance of A, and is responsible for managing the lifetime of that instance.
Is B an implementation of a unique pointer? There is an implementation of unique pointer provided by the standard: std::unique_ptr. I suggest using it instead of B.
Would it be more appropriate for the method B::get_a to return a constant reference:
Depends on whether it m_a == nullptr is valid state for B. If it is, then you would need to throw in that case if you wanted to return a reference.
Semantically, the difference between a pointer and a reference is that a pointer can be NULL. It basically comes down to whether the A* in your B class can ever be NULL. If yes, leave the code as is and return a pointer. If not, change the B ctor to take in the arguments for an A object, create the A object on the heap inside the B ctor and return a reference.

Downcasting a class c++

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.

Overriding function in C++ doesn't work

#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.

non-scalar type requested

can somebody please help me with an error
conversion from `A' to non-scalar type `B' requested
I have class A and derived from it B, but I have problems with these rows:
A a(1);
A *pb = new B(a);
B b = *pb; //here I have an error
thanks in advance for any help
class A {
protected:
int player;
public:
A(int initPlayer = 0);
A(const A&);
A& operator=(const A&);
virtual ~A(){};
virtual void foo();
void foo() const;
operator int();
};
class B: public A {
public:
B(int initPlayer): A(initPlayer){};
~B(){};
virtual void foo();
};
edited
I have this code and (I can't change it):
A a(1);
A *pb = new B(a);
B b = *pb;
I tried to create constructor for B:
B::B(const A & a):
player(a.player){}
B& B::operator=(const A& a){
if(this == &a){
return *this;
}
player = a.player;
return *this;
}
but it gives me an error, really need help from professionals
Your problem is due to static type checking. When you have this line:
A *pb = new B(a);
The static type of pb is A * and it's dynamic type is B *. While the dynamic type is correct, the compiler is checking the static type.
For this simple code, since you know the dynamic type of pb is always a B, you can fix this with a static cast:
B b = *static_cast<B *>(pb);
But be warned that if the dynamic type of pb was an A * the cast would cause undefined behavior.
When you dereference an 'A' pointer, you get an 'A' even if it points to a 'B'. Polymorphism does not come into play here! To preserve the 'B' properties to the 'A' object you should properly cast the initialization as explained in some of the other answers.
In such situations a dynamic cast is most appropriate. Dynamic cast will invoke the runtime type system to figure out the "real" type of bp and will return 0 if it can't be cast to the requested type. As you know the real type you could also use static_cast here but generally this isn't the case in such situations.
B* b = dynamic_cast<B*>(pb);
*pb will give you a A& and not a B&. It's just like pb being an A* and not a B* even though the actual object is a B.
B b = *pb will attempt to copy-construct a B using a synthesised copy constructor. The copy constructor will therefore be looking for a B& as its argument. You have no constructor to make a B from an A, hence the error.
As #R Samuel Klatchko says you could just cast it or, in the case you've given, you could just make pb actually be a B*.
You are trying to assign an object of type A to object of type B. Which is not allowed unless you define a type cast operator. pb is a pointer to A object and in general it is not a pointer to B (in your case it is, but it doesn't matter for the compiler, since it's declared as pointer to A). To make such assignement possible, you first need to down-cast pb to pointer of B (as R Samuel Klatchko pointed out, in your case static_cast is perfectly fine; in other cases you might need use dynamic_cast).

Why does this dynamic_cast of auto_ptr fail?

#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.