首先,何为钻石继承,顾名思义,在类的继承过程中,继承结构是一个类似菱形(钻石)的结构就属于钻石继承,如下:
这是一个最简单的钻石继承。实际上,在复杂的继承表中,只要子类按不同的继承路径回溯到基类有菱形结构,均属钻石继承。下面先看一个例子,钻石继承在C++程序设计中带来的问题。
2 #include<iostream>
3 using namespace std;
4 class A{
5 public:
6 A (int x) : m_x(x) {}
7 int m_x;
8 };
9 class B : public A {
10 public:
11 B (int x) : A(x) {}
12 void set(int x) {
13 this -> m_x = x;
14 }
15 };
16 class C : public A {
17 public:
18 C (int x) : A(x) {}
19 int get(void) {
20 return this -> m_x;
21 }
22 };
23 class D : public B,public C {
24 public:
25 D (int x) : B(x),C(x) {}
26 };
27 int main(void) {
28 D d(10);
29 d.set(20);
30 cout << d.get() << endl;
31 return 0;
32 }
33
这样的运行结果是10?还是20呢?结果是10,为什么?!明明sets的是20,为什么get的还是10呢?
要解释这个问题那酒必须要先搞清楚,d对象在内存中是如何存放的,是怎样布局的。每一个子类都会有一个内存视图,在子类里都包含了它的基类子对象,下面是创建是d对象时,d对象在内存中的存放形式。
包含一个B类的基类子对象和一个C类型基类子对象,而B和C里各自有一个A类型基类子对象,所以可以看到,在d的内存布局中有两个A类型基类子对象。
set函数是类B的成员函数,在执行set函数时,this指针指向B(其实也是指向A,B从A继承,A存在B中的首地址),所以set执行后,改变的是B里的A类基类子对象的数据成员的值。同理,get函数得到的是C里A类基类子对象的数据成员的值。这样就可以理解这样的运行结果了。所谓钻石继承问题,就是公共基类对象在我们最终的子类对象中有多个副本,多份拷贝,当我们沿着不同的继承路径去访问公共基类子对象时结果会出现不一致。
而我们应该怎样解决这样的问题呢?采用虚继承。我们所期望的d的存储形式:
我们需要按如下方式修改代码:
class C : virtual public A //虚继承
D(int x) : B(x),C(x),A(x) {}
这样就解决了。
在这个过程中,A对象只在D的初始化表中A(x)进行构造(虚基类最先被构造),而在B和C的初始化表中不再对A进行构造(实际上是都有一个指针指向了D中的A(x),来对A进行构造)。
钻石继承,在访问公共基类成员函数时,如果不是虚继承,还会引起二义性的错误。代码如下:
2 #include<iostream>
3 using namespace std;
4 class A{
5 public:
6 A (int x) : m_x(x) {}
7 void foo() {
8 cout << "A::foo()" << endl;
9 }
10 int m_x;
11 };
12 class B : public A {
13 public:
1:下列情况采用引用形参:需要在函数中修改实参的值;需要以大型对象作为实参传递,没法实现对象的复制。
2.使用引用形参还可以额外的信息。
3.应该将不需要修改的引用形参定义为const引用。因为非const引用形参既不能用const对象初始化,也不能用字面值或产生右值的表达式初始化。(左值右值傻傻分不清楚,找到一篇文章左值右值)
4.函数可以为形参定义默认实参,但如果有一个形参定义了默认实参,则他后面的所有形参都必须定义默认实参。在一个文件中,只能为一个形参指定默认实参一次。
5.内联机制适用于优化小的,只有几行的而且经常被调用的函数。内联函数应该在头文件中定义。
本文链接
用文本编辑器修改 /etc/inittab文件 ,把
代码:
id:5:initdefault:
这一行,修改成
代码:
id:3:initdefault:
输入startx 命令起动图形界面。