多重继承、钻石继承和虚继承
2016-06-16 22:15
393 查看
1、多重继承:一个子类同时继承自多个基类。例如:
电话 播放器 计算机
\ | /
智能手机
名字冲突:如果在子类的多个基类中,存在同名的标识符,而且子类又没有隐藏该名字,那么任何试图在子类中,或通过子类访问该名字的操作,都将引发歧义,除非通过作用域
限定操作符”::“显式指明所属基类,或者通过using声明构成重载。(前提条件,两个父类中的函数必须是符合重载的条件)
2、钻石继承:派生最终子类的多个中间基类源自一个公共基类
员工 ------公共基类
/ \
技术员 经理 -----中间基类
\ /
技术主管 -----最终子类
#include<iostream>
using namespace std ;
class A{
protected:
int m_i ;
} ;
class B:public A{
public:
void set(int i)
{
m_i = i ;
}
} ;
class C:public A{
public:
int get(){
return m_i ;
}
} ;
class D:public B,public C{} ;
int main()
{
D d ;
d.set(100) ;
cout << "d.get():" << d.get() <<endl ;
return 0;
}
这是因为在构造D类对象时,在d中的两个父类都有各自的A的存储区域,如下图:
派生多个中间基类(B和C)的公共基类(A)子对象,在继承自多个中间基类的最终子类(D)对象中存在多个实例。在最终子类中,或通过最终子类,访问公共基类中的成员,往往会因为继承路径的不同而导致数据不一致的问题。
为了解决这一问题,我们在继承的时候使用class C:virtual public A(虚继承)
虚继承:通过虚继承,可以保证公共基类子对象在最终子类对象中仅存一份实例,且为多个中间基类子对象所共享,进而避免钻石继承的数据不一致问题。如下图:
延伸两个重点:
A.终极构造
一般而言,子类的构造函数不能调用其间接基类的构造函数。
但是,一旦这个中间基类被声明为虚基类,它的所有子类都必须显式地调用该间接基类的构造函数,否则系统将试图为它的每个子类对象调用该间接基类的缺省构造函数。
class A {
public:
A (int data) : m_data (data) {}
int m_data;
};
class B : public A {
class B : virtual public A {
public:
B (int data) : A (/*data*/8888) {}
};
class D : public B {
public:
D (int data) : B (data), A (data) {}
};
int main (void) {
D d (1234);
cout << d.m_data << endl;
return 0;
}
B.阻断继承————构造无法派生出子类的类
class A
{
A(int data=0):m_data(data){}
int m_data ;
public:
void set(int data)
{
m_data=data ;
}
int get()const
{
return m_data ;
}
} ;
class B:public A{} ;
int main()
{
B b ;
b.set(1234) ;
cout << b.get() << endl ;
}
这个程序编译通不过,是因为在基类A中的构造函数是私有的,所以在创建对象时,子类无法调用基类的构造函数。
#include <iostream>
using namespace std;
class A {
A (int data = 0) : m_data (data) {}
public:
void set (int data) {
m_data = data;
}
int get (void) const {
return m_data;
}
private:
int m_data;
friend class AA;
};
class AA : virtual public A {
public:
AA (int data = 0) : A (data) {}
};
class B : public AA {};
int main (void) {
AA a;
a.set (1234);
cout << a.get () << endl;
B b;
return 0;
}
在上述函数中,AA类成为了一个不可继承的类。AA类可以创建自己的对象,但是由于AA类是A类的友元类,所以AA类可以通过初始化表的形式去调用A类中的私有构造函数,所以AA类可以像正常类一样去初始化,去做一个A类的子类。但是不能被其他类继承,因为AA类是虚继承与A类的,所以每一个AA类的派生类都会去调用A类的私有构造函数,所以无法正常继承。
电话 播放器 计算机
\ | /
智能手机
名字冲突:如果在子类的多个基类中,存在同名的标识符,而且子类又没有隐藏该名字,那么任何试图在子类中,或通过子类访问该名字的操作,都将引发歧义,除非通过作用域
限定操作符”::“显式指明所属基类,或者通过using声明构成重载。(前提条件,两个父类中的函数必须是符合重载的条件)
2、钻石继承:派生最终子类的多个中间基类源自一个公共基类
员工 ------公共基类
/ \
技术员 经理 -----中间基类
\ /
技术主管 -----最终子类
#include<iostream>
using namespace std ;
class A{
protected:
int m_i ;
} ;
class B:public A{
public:
void set(int i)
{
m_i = i ;
}
} ;
class C:public A{
public:
int get(){
return m_i ;
}
} ;
class D:public B,public C{} ;
int main()
{
D d ;
d.set(100) ;
cout << "d.get():" << d.get() <<endl ;
return 0;
}
这是因为在构造D类对象时,在d中的两个父类都有各自的A的存储区域,如下图:
派生多个中间基类(B和C)的公共基类(A)子对象,在继承自多个中间基类的最终子类(D)对象中存在多个实例。在最终子类中,或通过最终子类,访问公共基类中的成员,往往会因为继承路径的不同而导致数据不一致的问题。
为了解决这一问题,我们在继承的时候使用class C:virtual public A(虚继承)
虚继承:通过虚继承,可以保证公共基类子对象在最终子类对象中仅存一份实例,且为多个中间基类子对象所共享,进而避免钻石继承的数据不一致问题。如下图:
延伸两个重点:
A.终极构造
一般而言,子类的构造函数不能调用其间接基类的构造函数。
但是,一旦这个中间基类被声明为虚基类,它的所有子类都必须显式地调用该间接基类的构造函数,否则系统将试图为它的每个子类对象调用该间接基类的缺省构造函数。
class A {
public:
A (int data) : m_data (data) {}
int m_data;
};
class B : public A {
class B : virtual public A {
public:
B (int data) : A (/*data*/8888) {}
};
class D : public B {
public:
D (int data) : B (data), A (data) {}
};
int main (void) {
D d (1234);
cout << d.m_data << endl;
return 0;
}
B.阻断继承————构造无法派生出子类的类
class A
{
A(int data=0):m_data(data){}
int m_data ;
public:
void set(int data)
{
m_data=data ;
}
int get()const
{
return m_data ;
}
} ;
class B:public A{} ;
int main()
{
B b ;
b.set(1234) ;
cout << b.get() << endl ;
}
这个程序编译通不过,是因为在基类A中的构造函数是私有的,所以在创建对象时,子类无法调用基类的构造函数。
#include <iostream>
using namespace std;
class A {
A (int data = 0) : m_data (data) {}
public:
void set (int data) {
m_data = data;
}
int get (void) const {
return m_data;
}
private:
int m_data;
friend class AA;
};
class AA : virtual public A {
public:
AA (int data = 0) : A (data) {}
};
class B : public AA {};
int main (void) {
AA a;
a.set (1234);
cout << a.get () << endl;
B b;
return 0;
}
在上述函数中,AA类成为了一个不可继承的类。AA类可以创建自己的对象,但是由于AA类是A类的友元类,所以AA类可以通过初始化表的形式去调用A类中的私有构造函数,所以AA类可以像正常类一样去初始化,去做一个A类的子类。但是不能被其他类继承,因为AA类是虚继承与A类的,所以每一个AA类的派生类都会去调用A类的私有构造函数,所以无法正常继承。
相关文章推荐
- ifconfig
- 华为OJ平台——查找组成一个偶数最接近的两个素数
- 数学 ZJOI 2012 数列
- NET中的类型和装箱/拆箱原理
- Android Camera 浅析
- Java中从指定txt读取文件
- Python内建函数与对象方法
- web前端学习路线
- 执行shell脚本的方式
- 如何在Oracle中使用Java存储过程(详解)
- java缩小图片
- 信息增益
- Zynq入门基础实验一之Helloworld
- 想精度高,可以考虑用c语言中的函数gettimeofday
- Android studio动态调试smali
- 聊聊高并发系统之限流特技
- Qt属性系统
- nagios插件之监控tcp的established数量
- Linux shell提交github安装以及使用
- Android 音量竞争机制