Ghostblade
Ghostblade

Reputation: 145

About virtual function override in C++

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

Answers (3)

dan.p
dan.p

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

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385098

These are the rules for function overriding:

[C++11: 10.3/2]: If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides111 Base::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 function D::f overrides a function B::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 of D::f, or is an unambiguous and accessible direct or indirect base class of the class in the return type of D::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 of B::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

jrok
jrok

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

Related Questions