Android C++ Series: C++ Best Practices 4 Multiple Inheritance and Virtual Inheritance

Get into the habit of writing together! This is the 14th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the details of the event .

1. Background

When comparing Java and C++ at the syntax level, we have to mention the multiple inheritance of C++. We know that Android is single inheritance, and C++ is multiple inheritance. Multiple inheritance will inevitably be used in large projects. This article analyzes some features of C++ multiple inheritance.

2. How to implement multiple inheritance?

In C++, we can include multiple base classes in the derived list:

class Sub : public Base{
	...
}
class SubA : public Base1, public Base2{
	...
}
复制代码

A few notes about multiple inheritance:

  1. Each base class contains an optional access specifier;
  2. The derived class list can only contain classes that have already been defined; and these classes cannot be final;
  3. C++ has no special provisions on the cumulative number of inheritances that derived classes can inherit, but the same base class can only appear once in the derived class list. For example: Yes Sub:public Base1,public Base2,public Base3 ...., but not Sub:public Base1,Base1.

3. State inherited from each base class in multiple inheritance

In multiple inheritance, the subclass object contains subobjects of each base class. For example, Sub inherits Base1, Base2, and Base1 inherits from Base. Then the structure of the Sub object is as follows:

image-20220416220303128.png

Constructing an object of a derived class will simultaneously construct and initialize all of its base class sub-objects, and the constructor value of a multiple-inherited derived class will also initialize only its direct subclasses.

A subclass's constructor initializer list passes arguments to each immediate parent class individually. The construction order of the parent class is consistent with the order in which the base classes appear in the derived list, regardless of the order of the base classes in the derived class constructor initializer list. How to understand this sentence? That is, the construction order is related to class Sub : public Base1, public Base2this order, but Sub::Sub(std::string name):Base1(name),Base2(){}not related to.

In the new C++11 standard, a derived class is allowed to inherit constructors from one or several of its base classes, but if the same constructor is integrated from multiple base classes, the program will fail.

For example, Base1 and Base2 have const st::string&constructors with parameters, then Sub inherits Base1 at the same time, and Base2 will make an error. At this time, we must define our own constructor for Sub Sub:Sub(const std::string &s):Base1(s),Base2(s).

4. Type conversion in multiple inheritance

子类继承多个父类的情况下,我们可以令某个可访问基类的指针或引用直接指向一个子类对象。比如:

Base1 *base1 = new Sub();
Base2 *base2 = new Sub();
Base base = new Sub();
复制代码

编译器不会在子类像基类的几种转换中进行比较和选择,因为它认为转换成哪个父类都行。但是这样会带来二义性,比如重载方法时:

void action(const Base1&);
void action(const Base2&);
复制代码

当我们给action传Sub对象时就会出问题,因为编译器不知道怎么转换了。

所以我们在重载方法时要注意这种类型转换可能引起的问题。

5. 多重继承中的资源查找

在Java中我们查找属性时先从子类找,找不到再找父类,C++也是类似,但是在多重继承的结构中可能会有些复杂。

因为在查找过程中,会在所有直接基类中同时进行,如果名字在多个基类中都被找到,则这个属性的名字就产生了二义性。

我们思考一个问题,在Java中,如果我们定义了两个接口A,B,它们都有void test()这么一个方法,那么我们的Test类如果同时实现了A,B接口,具体的类该怎么实现呢?

在Java中确实比较简单,只需要实现一个test()方法就可以,但是在C++的多重继承中,父类不仅有方法还有属性,这种二义性该怎么解决呢?在C++中,对于一个派生类来说,从它的几个基类中分别继承名字相同的成员是完全合法的,只是需要在我们使用这些名字是加上前缀限定符明确指定它属于哪个基类(不调用不会出错,如果调用了还没有加前缀就会出错)。

比如上面说的test方法,我们的子类可以使用sub->Base1::text()方法来调用。

还有一种更复杂的情况,就是派生类继承的两个基类有函数名相同,但是参数列表不同的方法,这样查找是更容易出错。

**最佳实践:**为了避免二义性,除了我们在调用时加前缀,最好的办法是在派生类中为这个函数定义一个自己的版本,在函数内部来屏蔽这些二义性。比如:

int Sub::getMax() const
{
	return std::max(Base1::getMax(), Base2::getMax());
}
复制代码

6. 虚继承

我们在派生类列表中见过这么一种形式:

class Base1:public virtual Base{
...
}
复制代码

virtual代表虚继承,那么虚继承是做什么,要解决什么问题呢?

比如这样的一种结构:

class Base{
protected:
	int num;
}
class Base1:public Base{

}
class Base2:public Base{

}
class Sub:public Base1, Base2{

}
复制代码

In this case, Base is actually inherited twice, and by default the derived class contains subsections corresponding to each class in the inheritance chain. If a class appears multiple times during the derivation process, the derived class will contain multiple subobjects of the base class. The problem? In a word, a waste of resources.

And virtual inheritance solves this problem. Its purpose is to make a class make a declaration that it is willing to share its base class. We refer to the shared base class subobjects as virtual base classes. In this way, no matter how many times the virtual base class appears in the inheritance system, there is only one shared virtual base class subobject in the derived class.

**Best Practice:** In practical scenarios, the base class in the middle level declares its inheritance as virtual inheritance generally without negative problems. In this way, we can provide convenience for subsequent users or extensions.

7. Summary

The text introduces the difference between C++ multiple inheritance and Java implementing multiple interfaces, and introduces multiple inheritance and type conversion, resource lookup, and virtual inheritance in multiple inheritance.

Guess you like

Origin juejin.im/post/7087213179956101134