关于c++;多重继承 我的C++中有一些类似于的东西 class AbstractA { void Foo() = 0; void Bar() = 0; void Woo() = 0; }; class AbstractB : public AbstractA { void Doh() = 0; }; class Obj1 : public AbstractA { void Foo() {} void Bar() {} void Woo() {} };
现在我想定义一个新类关于c++;多重继承 我的C++中有一些类似于的东西 class AbstractA { void Foo() = 0; void Bar() = 0; void Woo() = 0; }; class AbstractB : public AbstractA { void Doh() = 0; }; class Obj1 : public AbstractA { void Foo() {} void Bar() {} void Woo() {} };,c++,syntax-error,multiple-inheritance,virtual-functions,C++,Syntax Error,Multiple Inheritance,Virtual Functions,现在我想定义一个新类Obj2,它是Obj1,并且是(实现)AbstractB。基本上 class Obj2 : public Obj1, public AbstractB { void Doh(); }; 这里发生编译错误 经过一些考虑,我怀疑我必须(重新)在Obj2内部定义Foo()、Bar()和Woo(),因为编译器不知道解决它们的正确路径(基本上是从Obj1或AbstractB?)。这一点我说得对吗 如果是这样的话,而且由于我解析Foo(),Bar()和Woo()的正确路径总是从
Obj2
,它是Obj1
,并且是(实现)AbstractB
。基本上
class Obj2 : public Obj1, public AbstractB {
void Doh();
};
这里发生编译错误
经过一些考虑,我怀疑我必须(重新)在Obj2
内部定义Foo()
、Bar()
和Woo()
,因为编译器不知道解决它们的正确路径(基本上是从Obj1
或AbstractB
?)。这一点我说得对吗
如果是这样的话,而且由于我解析Foo()
,Bar()
和Woo()
的正确路径总是从ObjA
传递,是否有任何语法可以避免对每个方法调用父类?换句话说,我能比你更简洁吗
class Obj2 : public Obj1, public AbstractB {
void Doh();
void Foo() { A::Foo() }
void Bar() { A::Bar() }
void Woo() { A::Woo() }
}
您可以使用
virtual
class Obj2 : public Obj1, public virtual AbstractB {
void Doh();
};
您可以使用
virtual
class Obj2 : public Obj1, public virtual AbstractB {
void Doh();
};
使用
=0
创建纯虚拟方法时,还需要使用virtual
关键字,例如
class AbstractA
{
virtual void Foo() = 0;
virtual void Bar() = 0;
virtual void Woo() = 0;
};
此外,您可能希望公开这些函数。
另一件非常重要的事情是始终在设计用于继承的类中声明虚拟析构函数,请参见示例:
类Obj1
实现接口AbstractA
,因此是一个具体的类AbstractB
使用方法Doh
扩展接口AbstractA
。
但是,当您继承Obj1
(继承AbstractA
)和AbstractB
(也继承AbstractA
)时,这会导致问题。因此,您继承了AbstractA“两次”,除非您使用虚拟继承,否则这不会起作用,请参见:
更简单的方法是不要让AbstractB
继承AbstractA
因此,声明类的一个好方法是:
class AbstractA
{
public:
virtual ~AbstractA() {}
virtual void Foo() = 0;
virtual void Bar() = 0;
virtual void Woo() = 0;
};
class AbstractB
{
public:
virtual ~AbstractB() {}
virtual void Doh() = 0;
};
/* This is now a concrete class that implements the 'interface' AbstractA. */
class Obj1 : public AbstractA
{
public:
void Foo() {} /* Implements AbstractA::Foo() */
void Bar() {} /* Implements AbstractA::Bar() */
void Woo() {} /* Implements AbstractA::Woo() */
};
/* Inherits Obj1's implementations of AbstractA and implements AbstractB */
class Obj2 : public Obj1, public AbstractB
{
public:
void Doh() {} /* Implements AbstractB::Woo() */
};
使用
=0
创建纯虚拟方法时,还需要使用virtual
关键字,例如
class AbstractA
{
virtual void Foo() = 0;
virtual void Bar() = 0;
virtual void Woo() = 0;
};
此外,您可能希望公开这些函数。
另一件非常重要的事情是始终在设计用于继承的类中声明虚拟析构函数,请参见示例:
类Obj1
实现接口AbstractA
,因此是一个具体的类AbstractB
使用方法Doh
扩展接口AbstractA
。
但是,当您继承Obj1
(继承AbstractA
)和AbstractB
(也继承AbstractA
)时,这会导致问题。因此,您继承了AbstractA“两次”,除非您使用虚拟继承,否则这不会起作用,请参见:
更简单的方法是不要让AbstractB
继承AbstractA
因此,声明类的一个好方法是:
class AbstractA
{
public:
virtual ~AbstractA() {}
virtual void Foo() = 0;
virtual void Bar() = 0;
virtual void Woo() = 0;
};
class AbstractB
{
public:
virtual ~AbstractB() {}
virtual void Doh() = 0;
};
/* This is now a concrete class that implements the 'interface' AbstractA. */
class Obj1 : public AbstractA
{
public:
void Foo() {} /* Implements AbstractA::Foo() */
void Bar() {} /* Implements AbstractA::Bar() */
void Woo() {} /* Implements AbstractA::Woo() */
};
/* Inherits Obj1's implementations of AbstractA and implements AbstractB */
class Obj2 : public Obj1, public AbstractB
{
public:
void Doh() {} /* Implements AbstractB::Woo() */
};
<>你试图解决在C++社区中臭名昭著的可怕的“死亡钻石”问题。Bjarne Stroustroup,C++的创建者,对这个问题作了一个经典的例证。
class Base {
public:
virtual void Method1() = 0;
virtual void Method2() = 0;
};
class Base1 : public Base
{
public:
virtual void Method1();
virtual void Method2();
};
class Base2 : public Base
{
public:
virtual void Method1();
virtual void Method2();
}
class Concrete : public Base1, public Base2
{
virtual void Method1();
virtual void Method2();
}
看看上面的类层次结构。正如您正确猜测的那样,编译器不知道应该将Method1()和Method2()的哪个版本纳入类的定义中。由于Base1和Base2都有各自版本的Method1()和Method2(),编译器有两个选项来选择这些定义,它只是感到困惑,开始抛出所有这些错误,并在所有地方抛出“不明确”一词
Bjarne Stroustroup针对这个问题提出的解决方案是一种称为“虚拟继承”的诡计。实际上,当您从类“Base”派生时,您需要做的是引入关键字virtual
这告诉编译器,尽管Base1和Base2中有多个Base副本,但它们实际上应该指向相同版本的Base。
这可确保在定义时:
混凝土类别:公共基础1、公共基础2
{
}
“Concrete”只获取“Base”的一个副本,“Base1”和“Base2”都指向该副本,从而解决了编译器的歧义。
编译器是如何实现这一点的?每个具有虚方法的类都与称为虚函数表或vtable的东西相关联。vtable有一个指向该类中所有虚拟方法的指针。加载类Base1和Base2时,这两个类的vtables包含一个指向基类的指针。同样,在加载混凝土时,混凝土的vtable也指向同一个基底实例,有效地确保存在一个基底实例
在实例化具体内容时,这里有几点需要注意。您必须显式调用Base的构造函数,就像显式调用Base1和Base2的构造函数一样。差不多
Concrete() : Base(), Base1(), Base2()
{
}
此外,如果在Base1和Base2的构造函数中有一个对基类()的显式调用,则在实例化具体的时候会跳过,而Basic的构造函数将被直接调用。
< P>您试图解决在C++社区中臭名昭著的问题,这是可怕的“死亡钻石”。Bjarne Stroustroup,C++的创建者,对这个问题作了一个经典的例证。class Base {
public:
virtual void Method1() = 0;
virtual void Method2() = 0;
};
class Base1 : public Base
{
public:
virtual void Method1();
virtual void Method2();
};
class Base2 : public Base
{
public:
virtual void Method1();
virtual void Method2();
}
class Concrete : public Base1, public Base2
{
virtual void Method1();
virtual void Method2();
}
看看上面的类层次结构。正如您正确猜测的那样,编译器不知道应该将Method1()和Method2()的哪个版本纳入类的定义中。由于Base1和Base2都有各自版本的Method1()和Method2(),编译器有两个选项来选择这些定义,它只是感到困惑,开始抛出所有这些错误,并在所有地方抛出“不明确”一词
Bjarne Stroustroup针对这个问题提出的解决方案是一种称为“虚拟继承”的诡计。实际上,当您从