具有相同名称的纯虚拟函数的不同实现
Distinct implementations for pure virtual functions with same name
可能重复:
继承共享方法名的接口
我有两个基类I1
和I2
,它们具有纯虚拟函数void R() = 0;
。我希望派生类IImpl
继承自I1
和I2
,并为I1::R()
和I2::R()
提供不同的实现。
下面的代码在MS VS 2005和2010中编译并工作。我编译时禁用了语言扩展,处于警告级别4。没有警告,也没有错误。
我在gcc 4.2中尝试了相同的代码。它不编译。GCC报告错误:
error: cannot define member function 'I1::R' within 'IImpl'
我的问题是:
- 为什么该代码在MSVS中有效,为什么在gcc中不起作用
- 该代码是标准C++吗
- 实现它的正确方法是什么,所以它是一个标准的C++并在VS和gcc上编译
谢谢!
#include <stdio.h>
class I1
{
public:
virtual void R() = 0;
virtual ~I1(){}
};
class I2
{
public:
virtual void R() = 0;
virtual ~I2(){}
};
class IImpl: public I1, public I2
{
public:
virtual void I1::R()
{
printf("I1::R()rn");
}
virtual void I2::R()
{
printf("I2::R()rn");
}
};
int main(int argc, char* argv[])
{
IImpl impl;
I1 *p1 = &impl;
I2 *p2 = &impl;
p1->R();
p2->R();
return 0;
}
根据8.3/1 ,此代码是非标准的
声明符id不应被限定,除非定义了其类之外的成员函数(9.3(或静态数据成员(9.4(,。。。
因此,在类中声明/定义成员函数名时,不能限定它们。这就是为什么它不是由gcc编译的。
MSVC似乎有一个允许此类代码的非标准扩展。我想这是为C++托管扩展所做的,这正是显式接口实现的方式,尽管它在很久以前就被弃用了,但语法似乎仍然受到支持。
Björn Pollex提供的链接描述了实现它的正确方式。
在IImpl
中只能实现一次void R()
。
IImpl impl;
I1 *p1 = &impl;
I2 *p2 = &impl;
p1->R();
p2->R();
这对我来说没有意义。R()
是一个虚拟方法,所以只要实际对象是IImpl
,在I1*
还是I2*
上调用它都无关紧要。此外,IImpl
有两个类似的R()
,您希望impl.R()
调用哪一个?我对标准还不够熟悉,不能引用它,但我确信你所做的是一个错误。如果我错了,请纠正我。
@紫罗兰长颈鹿
这是对Violet Giraffe的回答。它还消除了对后期绑定和MI的一些误解,后者比Java继承更灵活。
因此,只要实际对象是IImpl,那么在I1*或I2*上调用它都无关紧要。
// interfaces
class I1
{
public:
virtual void f () = 0;
};
class I2
{
public:
virtual void f () = 0;
};
// concrete implementations
class C1 : public I1
{
public:
virtual void f () { cout << "C1::f()n"; }
};
class C2 : public I2
{
public:
virtual void f () { cout << "C2::f()n"; }
};
// putting both together
class D : public C1, public C2
{
};
template <class I>
void f (I &i) {
i.f(); // virtual call to I::f()
}
int main() {
D d;
f<I1> (d); // virtual call to I1::f(): prints C1::f()
f<I2> (d); // virtual call to I2::f(): prints C2::f()
}
对成员函数的调用总是静态绑定到一个函数声明(此处为I1::f()
和I2::f()
(。在运行时,调用的函数(由称为"后期绑定"的进程调用(是该虚拟函数(此处为C1::f()
和C2::f()
(的派生次数最多的重写器(技术术语为"final"(。
在任何类中,每个纯虚拟函数最多必须有一个最终覆盖器。在非抽象类中,必须有每个虚拟函数的一个最终覆盖器。
现在您可以看到I1::f()
和I2::f()
是不同的函数,它们完全不相关;但两者具有相同的非限定名称和相同的参数,因此无法通过重载来区分它们。如果使用限定名称(d.I1::f()
和d.I2::f()
(,则调用将绑定到正确的声明,但虚拟性也将被禁止(在这种情况下,必须存在纯虚拟函数的定义,并且将被调用(。因此,调用这两个函数的唯一方法就是我在这里所做的:首先生成一个类型为I1
/I2
的左值,然后再进行一个不合格的调用.f()
。
附带说明:这种不同的覆盖在Java中是不可能的,因为MI是严格限制的,并且覆盖遵循不同的规则。
此外,IImpl有两个类似的R((,您希望为impl调用哪一个。R((?
您希望在d.f()
中调用哪个f()
?
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 如何在 C# 中实现C++虚拟纯类?
- 将C++子类成员函数(虚拟实现)传递给 C 类型函数指针
- 实现虚拟方法的组合行为
- 如何实现虚拟Inteface C
- 如何使用C 实现虚拟静态行为
- 允许其他基类实现虚拟函数
- 实现虚拟函数时对 vtable 的未定义引用
- 在 C++11 中实现虚拟方法条件
- 如何在c++中实现虚拟表
- 在没有虚拟关键字的情况下实现虚拟函数
- 如何在实现文件中实现虚拟方法
- 未实现虚拟功能
- 实现虚拟机C++的堆栈
- 在声明虚拟方法的父分支之外的其他父分支中实现虚拟方法
- C++在声明对象时可以实现虚拟方法吗
- 在继承中实现虚拟函数
- 从派生模板类中的基类重新实现虚拟函数
- 嵌入铬 - 实现虚拟功能C++