我对以下情况感到有点困惑,它们是否正确覆盖了函数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){...}
};
顺便说一句,访问权限的变化是否会有所不同?比方说,我这样写Derived类:
class Derived:public Base{
private://or protected:
virtual Base* copy(Base* b){...}
...
};
答案 0 :(得分:6)
这些是功能覆盖的规则:
[C++11: 10.3/2]:
如果虚拟成员函数vf
在类Base
和类Derived
中声明,直接或间接来自Base
,成员函数vf
,其中与Base::vf
具有相同名称,参数类型列表(8.3.5),cv-qualification和ref-qualifier(或不存在相同)声明,然后Derived::vf
也是虚拟的(无论是否如此声明)并且它覆盖 111Base::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)
覆盖Base::copy
可能会令人惊讶,因为它有不同的返回类型;只要两个返回类型为 covariant :
[C++11: 10.3/7]:
覆盖函数的返回类型应与被覆盖函数的返回类型相同,或协变与函数类相同。如果函数D::f
覆盖函数B::f
,则函数的返回类型如果满足以下条件则是协变的:
- 都是指向类的指针,都是对类的左值引用,或者两者都是对类的右值引用
- 返回类型
B::f
中的类与返回类型D::f
中的类相同,或者是返回类中返回类型的明确且可访问的直接或间接基类类型D::f
- 指针或引用具有相同的cv限定,并且返回类型
D::f
中的类类型具有与返回类型{{中的类类型相同的cv-qualification或更少的cv-qualification 1}}。
至于B::f
vs public
问题,没有规则说这很重要;如有任何疑问,请在脚注111中澄清情况:
111 具有相同名称但不同参数列表(第13条)作为虚函数的函数不一定是虚拟的,也不会覆盖。在覆盖函数的声明中使用
private
说明符是合法的但是冗余的(具有空语义)。 在确定覆盖时不考虑访问控制(第11条)。
答案 1 :(得分:3)
他们都是法律声明,只是这两个
virtual Base* copy(Derived* b);
virtual Derived* copy(Derived* b);
不要覆盖基类的copy
,因为它们的签名不同。他们只是宣布新的虚拟copy
隐藏了一个基础
然而,这一个
virtual Derived* copy(Base* b);
会覆盖。它有相同的签名和covariant return type。
在C ++ 11中,如果函数没有覆盖任何内容,你可以使用override
强制编译器发出错误:
virtual Derived* copy(Derived*) override { /*... */} // will produce an error
访问权限没有任何直接差异 - 它会根据对象的静态类型进行检查。如果base中的copy
是公共的并且你通过指向基类的指针调用它,它将调用合适的重写函数,即使它是私有的。
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
}
答案 2 :(得分:0)
在我写这篇文章的时候,已经出现了两个很好的答案,但我还是提交了我,因为它是用另一种风格写的。也许这个更浅的答案对某人有用。
首先,将copy
方法作为对象的一部分,将对象作为输入并返回一个对象,有点不清楚。它是从输入复制还是输入?它会返回副本还是自己?应该是static
吗?
您的所有声明都“有效”(取决于您希望实现的目标),但并非所有声明都在一起。
编辑:我删除了评论中有争议的部分,其他答案仍然涵盖了这一点。但我保留了这个部分给出一个例子来解释为什么不允许返回类型的多态。
要仅使用Derived
中的实现,您可以声明
class Derived:public Base{
public:
virtual Derived* copy(Base* b){...};
virtual Derived* copy(Derived* b){};
};
或
class Derived:public Base{
public:
virtual Base* copy(Base* b){...};
virtual Derived* copy(Derived* b){};
};
但是,C ++不支持基于返回类型的多态性。你不能使用
class Derived:public Base{
public:
virtual Base* copy(Derived* b){...};
virtual Derived* copy(Derived* b){};
};
因为如果不使用结果,编译器将无法确定要执行的操作。考虑一下:
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!
因为编译器无法决定在上一行中使用的版本,所以在返回类型上重载是非法的。
至于访问权限,我很乐意推荐其他答案。