1

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

2
  • 2
    Note that the new operator returns a pointer, so A a = new B; isn't type correct. You want either A a = B(); or A *a = new B();, though each does different things. One big difference is the former slices the B (en.wikipedia.org/wiki/Object_slicing). If A is abstract, only the latter is legal. Commented Mar 13, 2010 at 23:14
  • 1
    To have a pure virtual base class at least one functions needs to be defined like this: virtual int hello(A a) = 0; and all classes derived from that base class mush overwrite this function. You cant instantiate an object of the pure virtual base class. Commented Mar 14, 2010 at 6:02

6 Answers 6

6

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) 
Sign up to request clarification or add additional context in comments.

Comments

4

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.

Comments

2

First, A is not an abstract class in your code. It must have at least one pure virtual function to be abstract.

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

  2. A::hello() will be called. No problem with that, and parameter must be type A, as if there was no inheritance.

2 Comments

I never defined hello in class A, so that functions is pure virtual, hence is an abstract class. (Note that i declared and defined hello in B).
@ritmbo: no, that is not correct. To declare a pure virtual function you must write int hello(A a) = 0;. What you've done is declare a function, but never defined it. That's not the same thing at all.
0

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.

Comments

0

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.

3 Comments

If A derives from B, wouldn't the call to hello be ambiguous in the eyes of the compiler?
@Zach, no, it uses the most-derived one. Note that I'm assuming that the pass-by-value error is fixed (can't pass abstract classes by value).
It's specifically not overloading, because both function declarations are in different scopes. B::hello hides the hello in A. In your example case it's overloading only.
0

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.

3 Comments

Clarifications to the question should go into the question, rather than posted as an answer. SO is a Q&A site, not a forum.
If you declare B::hello as int hello(A a) and pass an instance of B, the instance will undergo slicing and become an A (anything specific to B will be discarded); dynamic_cast won't help. Pass by const reference (or use a pointer, but a const reference is preferred) to prevent slicing: int B::hello(const A& a).
Note that dynamic casting is dangerous: there's nothing preventing another coder from passing an A or C to B::hello. The point of typing is to prevent just that sort of thing from happening. Using dynamic_cast this way, and you might as well use an untyped language. Pay close attention to Ben Voigt's answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.