Goh*_*han 56 c++ gcc multiple-inheritance visual-c++
有两个基类具有相同的函数名.我想继承它们,并以不同的方式搭乘每种方法.如何使用单独的声明和定义(而不是在类定义中定义)?
#include <cstdio>
class Interface1{
public:
virtual void Name() = 0;
};
class Interface2
{
public:
virtual void Name() = 0;
};
class RealClass: public Interface1, public Interface2
{
public:
virtual void Interface1::Name()
{
printf("Interface1 OK?\n");
}
virtual void Interface2::Name()
{
printf("Interface2 OK?\n");
}
};
int main()
{
Interface1 *p = new RealClass();
p->Name();
Interface2 *q = reinterpret_cast<RealClass*>(p);
q->Name();
}
Run Code Online (Sandbox Code Playgroud)
我没能在VC8中移出定义.我发现Microsoft特定关键字__interface可以成功完成这项工作,代码如下:
#include <cstdio>
__interface Interface1{
virtual void Name() = 0;
};
__interface Interface2
{
virtual void Name() = 0;
};
class RealClass: public Interface1,
public Interface2
{
public:
virtual void Interface1::Name();
virtual void Interface2::Name();
};
void RealClass::Interface1::Name()
{
printf("Interface1 OK?\n");
}
void RealClass::Interface2::Name()
{
printf("Interface2 OK?\n");
}
int main()
{
Interface1 *p = new RealClass();
p->Name();
Interface2 *q = reinterpret_cast<RealClass*>(p);
q->Name();
}
Run Code Online (Sandbox Code Playgroud)
但还有另一种方法可以在其他编译器中使用更通用的东西吗?
Max*_*ert 68
这个问题不常出现.我熟悉的解决方案是由Doug McIlroy设计并出现在Bjarne Stroustrup的书中(在C++的设计和演变部分12.8和C++编程语言部分25.6中介绍).根据" 设计与进化"中的讨论,有一个提议优雅地处理这个特定情况,但它被拒绝了,因为"这样的名称冲突不太可能变得普遍,不足以保证单独的语言功能",并且"不太可能成为每天为新手工作."
你不仅需要调用Name()
通过指针到基类,你需要一个方式来表达其 Name()
想要在派生类中操作时.该解决方案增加了一些间接性:
class Interface1{
public:
virtual void Name() = 0;
};
class Interface2{
public:
virtual void Name() = 0;
};
class Interface1_helper : public Interface1{
public:
virtual void I1_Name() = 0;
void Name() override
{
I1_Name();
}
};
class Interface2_helper : public Interface2{
public:
virtual void I2_Name() = 0;
void Name() override
{
I2_Name();
}
};
class RealClass: public Interface1_helper, public Interface2_helper{
public:
void I1_Name() override
{
printf("Interface1 OK?\n");
}
void I2_Name() override
{
printf("Interface2 OK?\n");
}
};
int main()
{
RealClass rc;
Interface1* i1 = &rc;
Interface2* i2 = &rc;
i1->Name();
i2->Name();
rc.I1_Name();
rc.I2_Name();
}
Run Code Online (Sandbox Code Playgroud)
不漂亮,但决定是不经常需要它.
小智 6
您不能单独覆盖它们,您必须一次覆盖它们:
struct Interface1 {
virtual void Name() = 0;
};
struct Interface2 {
virtual void Name() = 0;
};
struct RealClass : Interface1, Interface2 {
virtual void Name();
};
// and move it out of the class definition just like any other method:
void RealClass::Name() {
printf("Interface1 OK?\n");
printf("Interface2 OK?\n");
}
Run Code Online (Sandbox Code Playgroud)
您可以使用中间基类模拟单个覆盖:
struct RealClass1 : Interface1 {
virtual void Name() {
printf("Interface1 OK?\n");
}
};
struct RealClass2 : Interface2 {
virtual void Name() {
printf("Interface2 OK?\n");
}
};
struct RealClass : RealClass1, RealClass2 {
virtual void Name() {
// you must still decide what to do here, which is likely calling both:
RealClass1::Name();
RealClass2::Name();
// or doing something else entirely
// but note: this is the function which will be called in all cases
// of *virtual dispatch* (for instances of this class), as it is the
// final overrider, the above separate definition is merely
// code-organization convenience
}
};
Run Code Online (Sandbox Code Playgroud)
此外,您正在使用reinterpret_cast错误,您应该:
int main() {
RealClass rc; // no need for dynamic allocation in this example
Interface1& one = rc;
one.Name();
Interface2& two = dynamic_cast<Interface2&>(one);
two.Name();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里是CRTP的重写,可能是你想要的(或不是):
template<class Derived>
struct RealClass1 : Interface1 {
#define self (*static_cast<Derived*>(this))
virtual void Name() {
printf("Interface1 for %s\n", self.name.c_str());
}
#undef self
};
template<class Derived>
struct RealClass2 : Interface2 {
#define self (*static_cast<Derived*>(this))
virtual void Name() {
printf("Interface2 for %s\n", self.name.c_str());
}
#undef self
};
struct RealClass : RealClass1<RealClass>, RealClass2<RealClass> {
std::string name;
RealClass() : name("real code would have members you need to access") {}
};
Run Code Online (Sandbox Code Playgroud)
但请注意,现在您无法在RealClass上调用Name(例如rc.Name()
,使用虚拟调度),您必须首先选择一个基数.自我宏是一种简单的方法来清理CRTP转换(通常成员访问在CRTP基础中更常见),但它可以改进.在我的其他一个答案中有一个关于虚拟调度的简短讨论,但如果有人有链接肯定会更好.
我不得不做这样的事情在过去,虽然在我的情况,我需要从一个接口继承两次,并能够在他们每个人的呼叫进行区分,我用一个模板垫片帮我...
像这样的东西:
template<class id>
class InterfaceHelper : public MyInterface
{
public :
virtual void Name()
{
Name(id);
}
virtual void Name(
const size_t id) = 0;
}
Run Code Online (Sandbox Code Playgroud)
然后,您从InterfaceHelper
两次而不是MyInterface
两次派生,并id
为每个基类指定一个不同的.然后,您可以通过转换为正确的方式独立地分发两个接口InterfaceHelper
.
你可以做一些稍微复杂的事情;
class InterfaceHelperBase
{
public :
virtual void Name(
const size_t id) = 0;
}
class InterfaceHelper1 : public MyInterface, protected InterfaceHelperBase
{
public :
using InterfaceHelperBase::Name;
virtual void Name()
{
Name(1);
}
}
class InterfaceHelper2 : public MyInterface, protected InterfaceHelperBase
{
public :
using InterfaceHelperBase::Name;
virtual void Name()
{
Name(2);
}
}
class MyClass : public InterfaceHelper1, public InterfaceHelper2
{
public :
virtual void Name(
const size_t id)
{
if (id == 1)
{
printf("Interface 1 OK?");
}
else if (id == 2)
{
printf("Interface 2 OK?");
}
}
}
Run Code Online (Sandbox Code Playgroud)
注意上面没有见过编译器......
归档时间: |
|
查看次数: |
29783 次 |
最近记录: |