This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is the slicing problem in C++?
I've got a simple code as a example of polymorphism and inheritance
class A
{
public:
int fieldInA;
void virtual overloadedFunc()
{
printf("You are in A\n");
}
};
class B : public A
{
public:
int fieldInB;
void overloadedFunc()
{
printf("You are in B\n");
}
};
void DoSmth(A* a)
{
a->overloadedFunc();
}
void DoSmthElse(A a)
{
a.overloadedFunc();
}
int _tmain(int argc, _TCHAR* argv[])
{
B *b1 = new B();
B b2;
//Breakpoint here
DoSmth(b1);
DoSmthElse(b2);
scanf("%*s");
return 0;
}
When I stop in breakpoint, the values of _vfptr[0] of b1 and _vfptr[0] of b2 are the same (SomeAddr(B::overloadedFunc(void))).
After passing b1 as parameter in DoSmth(), _vfptr[0] of local variable a is still someAddr(B::overloadedFunc(void)), but _vfptr[0] of a in DoSmthElse is now someAddr(A::overloadedFunc(void)). I'm sure this is some my misunderstaning of function overloading concept, but I couldn't understand, why in first case I saw "You are in B" and in second "You are in A". The same with A *b1 = new B(); DoSmth(b1); // You are in B, why?
First off, you need to get your terminology right! You didn't overload any functions, you overrode them:
Overloading means that you have the same function name with different types of arguments. Choosing the correct overload is a compile-time operation.
Overriding means that you have class hierarchy with a polymorphic (in C++ virtual) function and you replace the function being called with a function applicable to object of a more specialized class. You override the original meaning. Choosing the correct override is a run-time operation, in C++ using something similar to a virtual function table.
The terms are confusing enough and to make things worse, these even interact: At compile time the correct overload is chosen which way end up calling virtual function which may, thus, be overridden. Also, overrides a derived class may hide overloads otherwise inherited from the base class. All this may make no sense if you can't get the terms straight, though!
Now, for your actual problem, when you call DoSmthElse() you pass your object b2 by value to a function taking an object of type A. This creates an object of type A by copying the A subobject of your B object. But since B is derived from A, not all of B gets represented, i.e., the A object you see in DoSmthElse() doesn't behave like a B object but like an A object. After all, it is an A object and not a B! This process is typically called slicing: you slice off the parts of the B object which made it special.
To obtain polymorphic behaviour you need to call virtual functions on a pointer or reference to the base class, not an instance of a base class. When you call this function
void DoSmthElse(A a)
you passed an instance of B. This is pass by value and so the argument is a sliced copy of your B instance that you pass to it. Essentially this means that all of the properties of B that are common to A are preserved in this copy and all of the properties of B that are specific to B and not to A are lost. Because of this, object within DoSmthElse() that the function overloadedFunc() is called on is now exclusively of type A (and no longer of type B) and so of course A::overloadedFunc() is called.
In the first case with DoSmth when the argument is of type pointer to base class, the polymorphic behaviour is obtained as you would expect - the B* argument gets passed to the function and a copy of this pointer is made that is now of type A*. Although the copy has been cast to an A*, the object pointed to is still of type B. Because the object pointed to is of type B the fact that it is accessed via a pointer to it's base class, ensures that the function B::overloadedFunc() is actually called when the line
a->overloadedFunc();
is executed, because the virtual function overloadFunc() is overridden by class B. Had class B not implemented it's own distinct version of the base class virtual function (i.e. Class B overrides the class A functionality) then the base class version would have been called instead.
Related
In object slicing,when a derived class object is copied to a Base class object , does the _vptr of Derived class also gets copied to _vptr of Base class like other members of class Base?.If not why?
class Base{
public :
virtual void Display(){cout<<"In Base"<<endl;}
};
class Derived:public Base{
public:
void Display(){cout<<"In Derived"<<endl;}
};
int main()
{
Derived objD;
Base objB;
objB = objD;
objB.Display();
}
I have observed the following result for the above snippet.
Output
In Base
The vptr is NOT copied. Let's try to reason about why.
Looking at your main function:
Derived objD;
Base objB;
objB = objD; // <-- HERE
objB.Display();
In line 3, you are assigning objD to objB. This is actually calling Base's assignment operator (which is automatically defined):
Base& operator=(const Base& other)
and it is being passed objD as a Base&. So, your question becomes, "Does the assignment operator copy the vptr"? The answer is, "no". The default assignment operator only copies fields on the class.
You may then ask, "Why wouldn't it copy the vptr too?" The reason is that, if it copied the vptr, methods on the Base class would end up using methods implemented on the Derived class. However, in full generality, those methods could use data members that only exists on the Derived class (and that don't exist on the Base class). Calling those methods would therefore be nonsensical (the data logically doesn't exist on the Base class), and so the language rightly chooses not to do this.
The main issue is that, when you assign the Derived class to the Base class, the variable you're assigning to only holds the fields for the Base class, so the fields in the Derived class that aren't in the Base class are not copied. Therefore, methods from the Derived class won't make sense when called on the Base class.
Note that this isn't the case if, instead, you were to assign a Base pointer or a Base reference to the Derived class. In that case, the original Derived class instance still exists. It's sitting in memory somewhere and has all the Base+Derived class fields. Therefore, methods for the Derived class called on that instance will have access to those fields, and so being able to call those methods still makes sense.
This is why, in C++, to do polymorphism (via virtual methods), you need to use a reference or pointer. See here for a similar discussion:
Why doesn't polymorphism work without pointers/references?
All known implementations use vptr but the details vary a lot. The vptr is a way for implementations to represent the type of an object of a polymorphic class (class with virtual functions) and sometimes classes with virtual base classes (very implementation dependent).
Note that in many cases involving more than simple inheritance (single non virtual inheritance) classes have more than one vptr.
The vptr value is a function of the type of the most derived object constructed.
The type of an object cannot be changed by the user during its lifetime; during construction, the type of the object changes as it is being constructed; during destruction, it changes back. (It is illegal to refer to an object during construction or destruction with a name or other expression that has a type that doesn't match the object.)
You can reuse the space of an existing object to construct an object of a different type, but it isn't the same object:
struct X {
A a;
B b;
// requires: construction doesn't throw
};
void replace (X &x) {
x.~X();
B &b = *new (&x) B; // hope no exception here
b.f(); // use as a normal B object
x.f(); // undefined: cannot use x as a real object
X *p = &x; // legal: x isn't used as an object, only as an address
b.~B(); // clean up ressources if needed
new (&x) X; // valid: &x refers to storage, as if a void*
// x refers to a valid X object
}
Such object reuse doesn't change the type of any existing object: replace(x) doesn't just act on x (it does by destructing one and reconstructing one), it acts on the storage location of x. After calling replace(x) the name x can be used to refer to the newly constructed X object, which has effectively taken the identify of the previously existing X object.
No operation on an object (that keeps the object alive) is even allowed to change its type in C++. There is no way for a vptr to change on a constructed object.
Changing the type of a declared object would wreck the C++ types system and the invariants of declared objects. The compiler wouldn't know what class to destroy, which virtual functions to call, where the base classes are, etc. It would completely change the way C++ works. It's hard to even imagine the semantics associated with changing the type of an existing object.
I have 2 classes, A and B and I need an overwritten function in B to be called from A's constructor. Here is what I have already:
class A {
A(char* str) {
this->foo();
}
virtual void foo(){}
}
class B : public A {
B(char* str) : A(str) {}
void foo(){
//stuff here isn't being called
}
}
How would I get code to be called in B::foo() from A::A()?
I need an overwritten function in B to be called from A's constructor
This design is not possible in C++: the order of construction of a B object is that first the base A sub-object is constructed, then B is constructed on top of it.
The consequence is that, while in A constructor, you are still constructing an A object: any virtual function called at this point will be the one for A. Only when the A construction is finished and the B construction starts, will the virtual functions of B become effective.
For achieveing what you want, you have to use a two step pattern: 1) you construct the object, 2) you initialize it.
I think you are referring to Calling Virtuals During Initialization Idiom (aka Dynamic Binding During Initialization), so please have a look here, where everything is explained:
https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Calling_Virtuals_During_Initialization
https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom
2nd site has very good explanation, but it's way longer than 1st.
In a constructor, the base class' function will get called, not the overridden version. The reason for this is that, using your example, B's initialization is not complete when A's constructor is called, and thus calling B's foo would be done with an incomplete B instance if this were otherwise allowed.
Say I have base class A. it has method
void foo(A* a);
It also has method
void foo(B* b);
B inherits from A.
Say I now have a B instance but it is an A* ex:
A* a = new B();
If I were to call
someA.foo(a);
Would this call the A* implementation or B* implementation of the method?
I'm providing an A* to the method, but what that object actually is is a B().
Thanks
Function overloads are selected based on the static type of the passed parameter. The static type of a is A*, only its dynamic type is B. Go figure.
Well, two things will happen. First of all the determination of which function to call:
A* a = new B();
foo(a);
Here you pass a variable of type A* (C++ = static typed, remember) to foo, this will as usual call foo(A* a), nothing different from any other function overloading. If you were to call foo(new B()) it would use the implicit type B* and end up calling foo(B* b). Nothing new here, plain old function overloading. Note that only when foo(B*) is not present it will fall back to a more generic version because of inheritance.
Now in your example we come to the calling of this function:
void foo(A* a)
{
a->foo();
}
Well, again, standard C++ calling conventions apply, including polymorphism. This means if you have declared foo as virtual the vtable will be constructed in such a way that the foo method of B will be called for your example (as the object is created as type B). If A::foo() is not declared as virtual, the method of A itself will be called.
Because A::foo(A*) and B::foo(B*) have different signatures (types of their arguments), the compiler treats them as totally different functions. If instead of calling both methods foo() you had called one A::bar(A*) and another B::baz(B*), you would get identical behavior.
foo(A*) is a method of all objects of type A, and all objects of type B are also objects of type A because B is derived from A. So foo() is indeed a method of the object someA, inherited from the parent A class. This method, the A method, is the one that gets called.
I would expect it to use the foo(A *) method because the static type is A.
But, like someone said, add some logging and try checking which one is being executed.
Today I found the following disturbingly ambiguous situation in our code base:
class Base {
public:
virtual void Irrelevant_Function(void) = 0;
protected:
C_Container * Get_Container(void);
};
class A : public Base, public Not_Important {
public:
inline C_Container * Get_Container(void);
};
class B : public Base, protected SomethingElse {
public:
C_Container * Get_Container(void);
};
Many things were calling the Get_Container method, but not always calling the correct one - note that none of these functions were virtual.
I need to rename the methods Get_Base_Container, Get_A_Container, etc to remove the ambiguity. What rules does C++ use to determine which version of a function it should call? I'd like to start from the "known state" of what should have been getting called, and then figure out the bugs from there.
For example, if I have a pointer to a Base and call Get_Container, I assume it would just call the Base version of the function. What if I have a pointer to an A? What about a pointer to a B? What about an A or B on the heap?
Thanks.
It depends how you're calling the function. If you're calling through an A *, an A & or an A, then you'll be calling A::Get_Container(). If you're calling through a Base *, a Base & (even if they point to/reference an A), then you'll be calling Base::Get_Container().
As long as there's no virtual inheritance going on, it's quite easy. If you're working directly with an object, it's the object's method that gets called; if you're working with a pointer or reference, it's the type of the pointer or reference that determines the method, and the type of the object pointed to doesn't matter.
A method is first looked up according to the object's static type. If it is non-virtual there, you're done: that's the method that's called. The dynamic type is what virtual methods, dynamic_cast, and typeid use, and is the "actual" type of the object. The static type is what the static type system works with.
A a; // Static type and dynamic type are identical.
Base &a_base = a; // Static type is Base; dynamic type is A.
a.Get_Contaienr(); // Calls A::Get_Container.
a_base.Get_Container(); // Calls Base::Get_Container.
B *pb = new B(); // Static type and dynamic type of *pb (the pointed-to
// object) are identical.
Base *pb_base = pb; // Static type is Base; dynamic type is B.
pb->Get_Container(); // Calls B::Get_Container.
pb_base->Get_Container(); // Calls Base::Get_Container.
I've assumed above that the protected Base::Get_Container method is accessible, otherwise those will be compile errors.
A couple of additional points to note here:
Name lookup occurs in a single scope; E.g. When calling the method on an object with static type 'B', the compiler considers the interface of 'B' to determine whether or not there is a valid match. If there is not, it only then looks at the interface of Base to find a match. This is why that from the compiler's view, there is no ambiguity and it can resolve the call. If your real code has overloading etc. this may be an issue.
Secondly, it is often forgotten that the 'protected' keyword applies at class and not object level. So for example:
class Base {
protected:
C_Container * Get_Container(void);
};
class B : public Base{
public:
C_Container * Get_Container(void)
{
B b;
// Call the 'protected' base class method on another object.
return b.Base::Get_Container();
}
};
Suppose I have this:
class A
{
public:
virtual int hello(A a);
};
class B : public A
{
public:
int hello(B b){ bla bla };
};
So, A it's an abstract class.
1)In the class B, I'm defining a method that its suppose overrides the A class. But the parameter it's slightly different. I'm not sure about this, is this correct? Maybe because of polymorphism, this is ok but its rather confusing.
2) If I do: A a = new B;, and then a.hello(lol); if "lol" it's not of type B, then it would give compile error?, and if it's of type A from another class C (class C : public A), what would happend?
I'm confused about the overriding and virtual thing.. all examples I found work with methods without parameters.
Any answer, link, or whatever it's appreciated.
thanks
pd: sorry for my english
Your class B doesn't override the member function in A, it overloads it. Or tries to anyway, see the bit about hiding later.
Overriding is when a derived class defines its own version of a virtual member function from a base class. Overloading is when you define different functions with the same name.
When a virtual call is made on a pointer or reference that has the type of the base class, it will only "consider" overrides in the derived class, not overloads. This is essential - for an instance of B to be treated by callers as though it does everything an A can do (which is the point of dynamic polymorphism and virtual functions), its hello function needs to be able to take any object of type A. A hello function which only takes objects of type B, rather than any A, is more restrictive. It can't play the role of A's hello function, so it's not an override.
If you experiment a bit with calling hello on A and B, passing objects of type A or B, you should be able to see the difference. A has a function taking an A (which you haven't defined, so if you call it then your program will fail to link, but you can fix that). B has a function taking a B. They happen to have the same name, and of course since B derives from A, you can pass a B to the function taking an A. But B's function doesn't act as an override in virtual calls.
It is possible to call A's function on a B object, but only via a reference or pointer to A. A feature of C++ is that the definition of hello in B hides the definition in A. If overloading is what you want, it's possible to un-hide the base class function by adding using A::hello; to class B. If overriding is what you want, you have to define a function taking the same parameters. For example:
#include <iostream>
class A
{
public:
virtual int hello(A a) {std::cout << "A\n"; }
virtual int foo(int i) { std::cout << "A::Foo " << i << "\n"; }
};
class B : public A
{
public:
using A::hello;
// here's an overload
int hello(B b){ std::cout << "B\n"; };
// here's an override:
virtual int foo(int i) { std::cout << "B::Foo " << i << "\n"; }
};
int main() {
A a;
B b;
a.hello(a); // calls the function exactly as defined in A
a.hello(b); // B "is an" A, so this is allowed and slices the parameter
b.hello(a); // OK, but only because of `using`
b.hello(b); // calls the function exactly as defined in B
A &ab = b; // a reference to a B object, but as an A
ab.hello(a); // calls the function in A
ab.hello(b); // *also* calls the function in A, proving B has not overridden it
a.foo(1); // calls the function in A
b.foo(2); // calls the function in B
ab.foo(3); // calls the function in B, because it is overridden
}
Output:
A
A
A
B
A
A
A::Foo 1
B::Foo 2
B::Foo 3
If you take away the using A::hello; line from B, then the call b.hello(a); fails to compile:
error: no matching function for call to `B::hello(A&)'
note: candidates are: int B::hello(B)
A bunch of good answers telling you WHAT happens, I thought I'd jump in with WHY.
There's this thing called the Liskov Substitution Principle, which says that the function in the subclass has to work under the same preconditions and postconditions as the base class. In this case, the function has to be able to operate on any object of type A. Note that because of the inheritance relationships, every B is-a A, but not every A is-a B. So to replace the base method, the new function in the derived class can weaken the preconditions or strengthed the postconditions, but not strengthen preconditions or weaken postconditions.
Your attempt to override strengthens the precondition, it accepts Bs, not all As.
Note that covariance IS allowed on return types. If your base class returned A, then it guarantees that the return value is-a A. The base class could then return a B, because every B is-a A.
But for input parameters, only contravariance meets the theoretical requirements of the LSP, and in/out parameters are invariants. In C++ in particular, all parameter types are invariant for the purposes of overloading.
First, A is not an abstract class in your code. It must have at least one pure virtual function to be abstract.
different parameters means completely different method, even though the name is the same. Think of it as a different name. That's why it's called "signature". If A would be an abstract class, this code would not compile at all.
A::hello() will be called. No problem with that, and parameter must be type A, as if there was no inheritance.
When you override a method, it redefines what the method will do. You can only override virtual members that are already defined (with their set of parameters). If the type is of A, the method on A will be called. If the type is of B, the method on B will be called even if the variable is typed A but contains an instance of type B.
You can't change the parameter definitions for an overridden method, or else it would cease to be an override.
What you are doing there is overloading not overriding, i.e. it's as if class B is:
class B
{
public:
int hello(A a) {...}
int hello(B b) {...}
};
You have two functions of the same name, but with different signatures, which makes them different functions (just like the standard library has different functions for abs(float) and abs(double) etc.)
If you want to override, then you need to have the same signature, i.e. class B's hello needs to take a parameter of type A. That way, when you call hello on an object of class B, it will use class B's hello rather than class A's.
If you actually want class B's hello to only accept objects of type B then what you have is fine, although you probably want to make class A's hello non-virtual as you are not really wanting to override it -- you are defining a new function with new parameters and new behaviour.
Thansk for the answers, but I have to clarify some things to get my final answer.
Suppose I have the A class exactly how I defined it in the original question. And I add another method:
class A {
...
int yeah();
}
Then I define class B as the following:
class B : public A {
int hello(A a);
};
And another class C analogous to B.
What I know because I'm the programmer, it's that the hello methods of B and C are gonna have obviously A type objects as parameters, but instances of the same class.
In example:
B b;
b.hello(some_other_b_instance);
or
C c;
c.hello(some_other_c_instance);
The problem is that in each hello function of the classes B and C, I want to do particular things with atributes of the particular class B or C. And because of the parameter is of type A, I cannot use them.
What I would need it's a kind of inverse polymorphysm, but its wrong because by definition I can send a C instance to B hello class, but I know it's not gonna happend.
I hope you get the idea of the code... A clase is abstract, and the real work makes sense in the particular clases B and C, each one do the work in their particular way to make the yeah function work. But B and C need to access their members to do the hello work correctly.