Reputation: 145
I'm a little confused about the following situations, are they correct ways for the overriding of function copy
but not overloading, or all of them are right?
class Base{
public:
virtual Base* copy(Base* b){...}
};
class Derived:public Base{
public:
virtual Base* copy(Base* b){...}//I know this should work
// but how about the followings?
//virtual Base* copy(Derived* b){...}
//virtual Derived* copy(Base* b){...}
//virtual Derived* copy(Derived* b){...}
};
BTW, does the change of access right make any difference? say, I write the Derived class like this:
class Derived:public Base{
private://or protected:
virtual Base* copy(Base* b){...}
...
};
Upvotes: 0
Views: 1422
Reputation: 416
There already popped up two good answers while i was writing this, but i submit my anyway because it is written in another style. Maybe this more shallow answer is useful to someone.
First of all, it is a bit unclear with the copy
method being part of an object, taking an object as input, and returning an object. Does it copy from or to the input? Does it return a copy or itself? Is it supposed to be static
?
All of your declarations "work" (depending on what you wish to achieve), but not all of them together.
Edit: I removed the part disputed in the comments, the other answers covers that anyway. But i kept the part giving an example to explain why polymorphism on return type isn't allowed.
To only use implementations in Derived
, you can declare
class Derived:public Base{
public:
virtual Derived* copy(Base* b){...};
virtual Derived* copy(Derived* b){};
};
or
class Derived:public Base{
public:
virtual Base* copy(Base* b){...};
virtual Derived* copy(Derived* b){};
};
Polymorphism based on return type is not supported in C++, however. You cannot use
class Derived:public Base{
public:
virtual Base* copy(Derived* b){...};
virtual Derived* copy(Derived* b){};
};
because the compiler will have trouble determining what to do if you do not use the result. Consider:
Derived * d = new Derived();
Derived * toCopy = new Derived();
Base * b2 = toCopy->copy(d); // Should use use the version returning Base
Derived * d2 = toCopy->copy(d); // Should use the version returning Derived
toCopy->copy(d2); // Which implementation should the compiler pick? It cannot know!
Because the compiler cannot decide on the version to use in the last line above, it is illegal to overload on return type.
As for the access right, i gladly recommend the other answers.
Upvotes: 0
Reputation: 385098
These are the rules for function overriding:
[C++11: 10.3/2]:
If a virtual member functionvf
is declared in a classBase
and in a classDerived
, derived directly or indirectly fromBase
, a member functionvf
with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) asBase::vf
is declared, thenDerived::vf
is also virtual (whether or not it is so declared) and it overrides111Base::vf
. [..]
If these rules are not met, then the new function does not override the old function (though it may overload or hide it).
So:
class Base
{
public:
virtual Base* copy(Base* b);
};
class Derived : public Base
{
public:
// Overrides Base::copy
virtual Base* copy(Base* b);
// Does NOT override Base::copy (due to different parameter-type-list)
virtual Base* copy(Derived* b);
// Overrides Base::copy (despite different return type)
virtual Derived* copy(Base* b);
// Does NOT override Base::copy (due to different parameter-type-list)
virtual Derived* copy(Derived* b);
private:
// Overrides Base::copy (despite different access specifier)
virtual Base* copy(Base* b);
};
Though, note that the above class Derived
is actually ill-formed, due to the end of 10.3/2
which states:
In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.
That means we should only have declared one of those overriding functions. I listed them all inside a single class definition just for the purpose of illustration.
It may be surprising that virtual Derived* copy(Base* b)
overrides Base::copy
, because it has a different return type; this is allowed as long as the two return types are covariant:
[C++11: 10.3/7]:
The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. If a functionD::f
overrides a functionB::f
, the return types of the functions are covariant if they satisfy the following criteria:
- both are pointers to classes, both are lvalue references to classes, or both are rvalue references to classes
- the class in the return type of
B::f
is the same class as the class in the return type ofD::f
, or is an unambiguous and accessible direct or indirect base class of the class in the return type ofD::f
- both pointers or references have the same cv-qualification and the class type in the return type of
D::f
has the same cv-qualification as or less cv-qualification than the class type in the return type ofB::f
.
As for the public
vs private
question, there is no rule saying that this matters; the situation is clarified by footnote 111 in case there was any doubt:
111 A function with the same name but a different parameter list (Clause 13) as a virtual function is not necessarily virtual and does not override. The use of the
virtual
specifier in the declaration of an overriding function is legal but redundant (has empty semantics). Access control (Clause 11) is not considered in determining overriding.
Upvotes: 6
Reputation: 55395
They're all legal declarations, it's just that these two
virtual Base* copy(Derived* b);
virtual Derived* copy(Derived* b);
do not override the copy
from the base class, as their signature is different. They just declare new virtual copy
that hides the one from base.
This one, however
virtual Derived* copy(Base* b);
does override. It's got the same signature and a covariant return type.
In C++11 you can use override
to force the compiler to emit an error if the function does not override anything:
virtual Derived* copy(Derived*) override { /*... */} // will produce an error
Access right doesn't make any direct difference - it is checked based on the static type of the object. If the copy
in base is public and you call it through a pointer to base class, it'll call the suitable overriding function even if it were private.
class Base {
public:
virtual Base* copy(Base* b);
};
class Derived : public Base {
private:
virtual Base* copy(Base* b); // Overrides Base::copy
};
int main()
{
Base* b = new Derived;
Base* b2;
b->copy(b2); // calls Derived::copy
Derived d;
d.copy(b2); // error, as expected
}
Upvotes: 3