0

我对以下情况有点困惑,它们是覆盖函数copy而不是重载的正确方法,还是它们都是正确的?

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){...}
};

顺便说一句,访问权限的更改有什么不同吗?说,我这样写派生类:

class Derived:public Base{
private://or protected:
    virtual Base* copy(Base* b){...}
    ...
};
4

3 回答 3

6

这些是函数覆盖的规则:

[C++11: 10.3/2]:如果在一个类和一个类vf中声明了一个虚拟成员函数,直接或间接派生自一个具有相同名称、参数类型列表 (8.3.5)、cv-qualification 和 ref-qualifier 的成员函数(或没有相同的)作为声明,然后也是虚拟的(无论是否如此声明)并且它覆盖111[..]BaseDerivedBasevfBase::vfDerived::vf Base::vf

如果不满足这些规则,则新函数不会覆盖旧函数(尽管它可能会重载隐藏它)。

所以:

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);
};

不过,请注意,上面的类Derived实际上是格式错误的,因为它的结尾10.3/2指出:

在派生类中,如果基类子对象的虚成员函数具有多个最终覆盖器,则程序格式错误。

这意味着我们应该只声明其中一个覆盖函数。出于说明的目的,我将它们全部列在一个类定义中。

virtual Derived* copy(Base* b)overrides可能令人惊讶Base::copy,因为它具有不同的返回类型;只要两种返回类型是协变的,这是允许的:

[C++11: 10.3/7]: 覆盖函数的返回类型应与被覆盖函数的返回类型相同或与函数的类协变如果函数D::f覆盖函数B::f,则函数的返回类型如果满足以下条件,则它们是协变的:

  • 两者都是指向类的指针,都是对类的左值引用,或者都是对类的右值引用
  • 的返回类型中的类与 的返回类型中B::f的类相同D::f,或者是返回类型中的类的明确且可访问的直接或间接基类D::f
  • 两个指针或引用都具有相同的 cv 限定,并且返回类型中的类类型具有与 的返回类型中的类类型D::f相同或更少的 cv 限定B::f

至于publicvsprivate问题,没有规则说这很重要;如果有任何疑问,脚注 111 澄清了这种情况:

111作为虚函数,同名但参数列表不同的函数(第 13 条)不一定是虚函数,也不会覆盖。在覆盖函数的声明中使用说明virtual符是合法的但多余的(具有空语义)。在确定覆盖时不考虑访问控制(第 11 条)。

于 2013-08-24T16:43:33.453 回答
3

都是合法的声明,只是这两个

virtual Base* copy(Derived* b);
virtual Derived* copy(Derived* b);

不要覆盖copy基类中的 ,因为它们的签名不同。他们只是声明了一个新的虚拟copy,它从基地隐藏了一个。
不过这个

virtual Derived* copy(Base* b);

确实覆盖。它具有相同的签名和协变返回类型

override在 C++11 中,如果函数没有覆盖任何内容,您可以使用它来强制编译器发出错误:

virtual Derived* copy(Derived*) override { /*...  */} // will produce an error

访问权限没有任何直接的区别——它是根据对象的静态类型进行检查的。如果copyin 基类是公共的,并且您通过指向基类的指针调用它,即使它是私有的,它也会调用合适的覆盖函数。

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
}
于 2013-08-24T16:43:54.027 回答
0

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.

于 2013-08-24T17:12:18.763 回答