在 C++ 中用派生类型覆盖成员函数
Overriding a member function with derived type in C++
是否可以使用类似于 B::g()
或 B::h()
中显示的派生 class 覆盖参数?
如果没有,是否有实现相同目的的明智方法?
为什么B::i()
不起作用,但为什么其他的不起作用?
我的目标是在 B::g()
中使用 r
作为类型 Y
。
struct X {};
struct Y : X {};
struct A {
virtual void f(X q);
virtual void g(X& r);
virtual void h(X* s);
virtual X i(); // note: overridden function is 'virtual X A::i()'
virtual X* j();
};
struct B final : A {
void f(Y q) final; // error: 'void B::f(Y)' marked 'final', but is not virtual
void g(Y& r) final; // error: 'void B::g(Y&)' marked 'final', but is not virtual
void h(Y* s) final; // error: 'void B::h(Y*)' marked 'final', but is not virtual
Y i() final; // error: invalid covariant return type for 'virtual Y B::i()'
Y* j() final; // works
};
为了覆盖一个函数参数列表必须相同,但在你的情况下,它不是。这就是为什么它被认为是一个新函数并且不会覆盖父函数的原因。
所以你需要有这样的东西来覆盖:
struct A {
virtual void f(X q);
virtual void g(X& r);
virtual void h(X* s);
virtual X i();
virtual X* j();
};
struct B final : A {
void f(X q) override final;
void g(X& r) override final;
void h(X* s) override final;
X i() override final;
X* j() override final;
// Y* j() override final; <-- Also works because of covariant return
};
但请记住,您仍然可以将 Y
传递给这些函数,因为 Y
继承了 X
。
作为最后一个 class 成员的旁注 ,Y* j() override final;
也可以,因为它将是 Covariant return。对于协变 return,您的 return 需要是指针或引用。但是你需要看看你是否真的需要它。
My goal is to use r in B::g() as type Y.
您可以这样做,但是 B::g
不会覆盖 A::g
。如果是这样,您需要使用相同的参数类型:
struct A {
virtual void g(X& r);
virtual ~A(){}
};
struct B : A {
void g(X& r) override;
};
要覆盖标记为 final
的内容,您需要在开头添加虚拟。
例如:virtual void f(Y q) final;
B::i()
不起作用,因为 return 类型与您要覆盖的函数的 return 类型不同。
但是 B::j()
起作用的原因是因为 return 类型是一个指针,从技术上讲,您可以 return 一个指向所有类型的指针。
您只能以一种仍然可以安全地使用派生 class 代替基 class 的方式覆盖虚函数(例如,通过指向基的指针)。
让我们以您提供的 classes 为例:
B b;
A *a = &b;
X x;
a->g(x);
在最后一行中,无法确保 g()
的调用者将按 B
的预期传递 Y
,因为调用者正在使用基础 class' 接口,这是虚函数和动态多态性的全部要点。
另一方面,return 类型在派生的 class:
中更具体是安全的
B b;
A *a = &b;
X *x = a->j();
最后一条语句仍然是类型安全的,即使 j()
实际上是 returns Y*
。此 C++ 功能称为 covariant return type.
您还可以阅读有关类型系统中的协变和逆变的更多信息here。
仅当覆盖函数具有与继承函数相同的签名(参数类型、const
限定符等)时才能覆盖虚函数。
所以
struct A
{
virtual void f(X &);
};
struct B : public A
{
virtual void f(X &);
};
是可能的,但要更改 f()
的签名,如
struct B : public A
{
virtual void f(Y &);
};
实际上隐藏了继承的 f()
而不是覆盖它。使用 override
标识符,编译器会检测到这不是覆盖,并诊断错误。
如果正确覆盖,可以将 Y
传递给 A::f()
或 B::f()
,因为您的 Y
派生自 X
。但是不可能用具有与 A::f()
.
不同类型参数的函数覆盖 A::f()
有一个函数的特殊情况 returns 一个指向基数 class 的指针(或者,一个引用)——这可以被一个函数覆盖 returns 指向派生 class 的指针。参数必须具有相同类型的约束。因此,您的 B::j()
有效,因为 Y
派生自 X
。就编译器而言,像
这样的调用
X*x = pA->j(); // pA is of type A* but points at a B
没问题,因为它仍然是 returns 一个可以有效转换为 X *
的指针
是否可以使用类似于
B::g()
或B::h()
中显示的派生 class 覆盖参数?如果没有,是否有实现相同目的的明智方法?
为什么
B::i()
不起作用,但为什么其他的不起作用?
我的目标是在 B::g()
中使用 r
作为类型 Y
。
struct X {};
struct Y : X {};
struct A {
virtual void f(X q);
virtual void g(X& r);
virtual void h(X* s);
virtual X i(); // note: overridden function is 'virtual X A::i()'
virtual X* j();
};
struct B final : A {
void f(Y q) final; // error: 'void B::f(Y)' marked 'final', but is not virtual
void g(Y& r) final; // error: 'void B::g(Y&)' marked 'final', but is not virtual
void h(Y* s) final; // error: 'void B::h(Y*)' marked 'final', but is not virtual
Y i() final; // error: invalid covariant return type for 'virtual Y B::i()'
Y* j() final; // works
};
为了覆盖一个函数参数列表必须相同,但在你的情况下,它不是。这就是为什么它被认为是一个新函数并且不会覆盖父函数的原因。
所以你需要有这样的东西来覆盖:
struct A {
virtual void f(X q);
virtual void g(X& r);
virtual void h(X* s);
virtual X i();
virtual X* j();
};
struct B final : A {
void f(X q) override final;
void g(X& r) override final;
void h(X* s) override final;
X i() override final;
X* j() override final;
// Y* j() override final; <-- Also works because of covariant return
};
但请记住,您仍然可以将 Y
传递给这些函数,因为 Y
继承了 X
。
作为最后一个 class 成员的旁注 ,Y* j() override final;
也可以,因为它将是 Covariant return。对于协变 return,您的 return 需要是指针或引用。但是你需要看看你是否真的需要它。
My goal is to use r in B::g() as type Y.
您可以这样做,但是 B::g
不会覆盖 A::g
。如果是这样,您需要使用相同的参数类型:
struct A {
virtual void g(X& r);
virtual ~A(){}
};
struct B : A {
void g(X& r) override;
};
要覆盖标记为 final
的内容,您需要在开头添加虚拟。
例如:virtual void f(Y q) final;
B::i()
不起作用,因为 return 类型与您要覆盖的函数的 return 类型不同。
但是 B::j()
起作用的原因是因为 return 类型是一个指针,从技术上讲,您可以 return 一个指向所有类型的指针。
您只能以一种仍然可以安全地使用派生 class 代替基 class 的方式覆盖虚函数(例如,通过指向基的指针)。
让我们以您提供的 classes 为例:
B b;
A *a = &b;
X x;
a->g(x);
在最后一行中,无法确保 g()
的调用者将按 B
的预期传递 Y
,因为调用者正在使用基础 class' 接口,这是虚函数和动态多态性的全部要点。
另一方面,return 类型在派生的 class:
中更具体是安全的B b;
A *a = &b;
X *x = a->j();
最后一条语句仍然是类型安全的,即使 j()
实际上是 returns Y*
。此 C++ 功能称为 covariant return type.
您还可以阅读有关类型系统中的协变和逆变的更多信息here。
仅当覆盖函数具有与继承函数相同的签名(参数类型、const
限定符等)时才能覆盖虚函数。
所以
struct A
{
virtual void f(X &);
};
struct B : public A
{
virtual void f(X &);
};
是可能的,但要更改 f()
的签名,如
struct B : public A
{
virtual void f(Y &);
};
实际上隐藏了继承的 f()
而不是覆盖它。使用 override
标识符,编译器会检测到这不是覆盖,并诊断错误。
如果正确覆盖,可以将 Y
传递给 A::f()
或 B::f()
,因为您的 Y
派生自 X
。但是不可能用具有与 A::f()
.
A::f()
有一个函数的特殊情况 returns 一个指向基数 class 的指针(或者,一个引用)——这可以被一个函数覆盖 returns 指向派生 class 的指针。参数必须具有相同类型的约束。因此,您的 B::j()
有效,因为 Y
派生自 X
。就编译器而言,像
X*x = pA->j(); // pA is of type A* but points at a B
没问题,因为它仍然是 returns 一个可以有效转换为 X *