我有两个带有纯虚函数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'
我的问题是:
谢谢!
#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()\r\n");
}
virtual void I2::R()
{
printf("I2::R()\r\n");
}
};
int main(int argc, char* argv[])
{
IImpl impl;
I1 *p1 = &impl;
I2 *p2 = &impl;
p1->R();
p2->R();
return 0;
}
答案 0 :(得分:6)
根据8.3 / 1
,此代码是非标准的除了在其类之外的成员函数(9.3)或静态数据成员(9.4)的定义之外,声明者id不应被限定,...
因此,在类中声明/定义成员函数名时,您无法限定成员函数名。这就是为什么它不是由gcc编译的。
MSVC似乎有一个允许此类代码的非标准扩展。我想它是针对C ++的Managed Extensions完成的,这正是在那里进行显式接口实现的方式,虽然它很久以前就被弃用了但似乎仍然支持语法。
在BjörnPollex提供的link中描述了实施它的正确方法。
答案 1 :(得分:1)
您只能在void R()
中实施一次IImpl
。
答案 2 :(得分:1)
IImpl impl;
I1 *p1 = &impl;
I2 *p2 = &impl;
p1->R();
p2->R();
这对我没有意义。 R()
是虚拟方法,因此只要真实对象为{{1},您是在I1*
还是I2*
上调用它并不重要}。
此外,IImpl
有两个相似的IImpl
,你期望为R()
调用一个impl.R()
?
我不太熟悉这个标准引用它,但我确定你所做的是一个错误。
如果我错了,请纠正我。
答案 3 :(得分:1)
@Violet Giraffe
这是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()
)。在运行时,调用的函数(通过名为&#34;过程绑定&#34;的过程)是此虚函数的最多派生覆盖(技术术语是&#34; final&#34;) (此处C1::f()
和C2::f()
)。
在任何课程中,每个纯虚拟函数必须有最多一个最终覆盖。在非抽象类中,每个虚函数必须有恰好一个最终覆盖。
现在你看到I1::f()
和I2::f()
是不同的功能,而且它们完全不相关;但两者都具有相同的非限定名称和相同的参数,因此无法通过重载来区分它们。如果使用限定名称(d.I1::f()
和d.I2::f()
),则调用将绑定到正确的声明,但虚拟内容也将被禁止(在这种情况下,必须存在纯虚函数的定义)并将被称为)。因此,调用这两种函数的唯一方法就是我在这里所做的:首先创建一个I1
/ I2
类型的左值,然后才进行无限制的调用.f()
。
附注:在Java中这种不同的覆盖是不可能的,因为MI是严格限制的,并且覆盖遵循不同的规则。
此外,IImpl有两个相似的R(),你期望为impl.R()调用哪一个?
您希望在f()
中调用哪个d.f()
?