继承
对于
private
,protected
,public
三种继承方式,除了基类的私有成员在派生类中不可直接访问外,其他访问权限在派生类中均可直接访问
class A {
int a;
public:
A(int n):a(n){};
int get(){ return a;}
};
class B:protected A
{
int b;
public:
B(int m,int n): A(n),b(m){}
void getb(){
cout<<b<<endl;
cout<<get();
}
};
//在类B中,还隐含了
private: int a;
protected: int get(){ return a;}
私有继承
基类的所有成员在派生类中均变成private
,而这些private
又可以分为在基类中原本是private
和原本是
proteceted
以及public
;
前者在派生类中不能直接访问,一般是通过继承下来可以访问基类私有成员的公有成员函数,进而获得基类的私有成员;而后者,在派生类中是可以直接访问;
保护继承
和私有继承类似,在派生类中,继承下来的基类的private
成员不能直接访问,其余可以;
在派生类外,由于基类的public
成员也变成protected
,就无法访问了,即派生类的对象无法访问任何基类的成员
公有继承
在派生类中,继承下来的成员的访问权限不变,所以派生类的对象可以访问基类的public
成员
三种继承方式,一般都使用protected
继承方式
- 对于私有继承,基类的
protected
成员在派生类中变成private
,也还能访问;但派生链中,派生类的派生类就无法访问基类的任何成员,所以,一旦出现私有继承,类内直接访问的特性就无法在派生类中传递下去 protected
继承的优点是:既可以在本类中实现数据的隐藏(类内可以直接访问,类外不可被访问),又可以将其类内直接访问特性继续传递到派生类中(在派生类中可以直接访问,类外不可直接访问)
多重继承
class <派生类> : <继承方式> <基类1>, <继承方式> <基类2>, <继承方式> <基类3> ...
{
···
};
基类成员初始化
构造函数
class B : protected A, protected C,protected E
{
···
public:
B(args):E(arg1),C(arg2),A(arg3){ }
};
派生类构造函数执行顺序,先构造基类,再构造自身,而基类的构造顺序取决于继承顺序,而不是派生类构造函数的初始化列表顺序
析构函数
派生类的析构顺序和自身的构造顺序相反
基类的构造函数和析构函数不会被派生类继承,而只能通过派生类的构造函数或析构函数自动调用,完成对基类数据成员的初始化或清理工作
二义性和支配规则
二义性
在多重继承中,当在派生类中出现两个以上同名的可直接访问的基类成员时,便出现了二义性,也称之为访问冲突
class A{
protected:
int x;
public:
void show(){ cout<<x;}
A(int n):x(n){}
};
class B{
protected:
int x;
public:
void show(){ cout<<x;}
B(int n):x(n){}
};
class C:protected A,protected B{
int c;
public:
void setA(int a) { x=a; } //出现
void setB(int b) { x=b; } //二义性
void setc(int c) { this->c=c; }
C(int a,int b,int c):A(a),B(b){this->c = c;}
C(){};
};
int main(){
C c;
c.setA(100);
c.setB(200);
c.show() //类外出现二义性
}
A类和B类具有同名的x
和show()
,他们均被继承到C类中,当C类对象访问时,就出现二义性。
解决方法:
- 改名
- 在派生类中使用作用域运算符
第1点明显不是个好办法
使用作用域运算符
void setA(int a){ A::x = a;}
void setB(int b){ B::x = b;}
c.A::show();
c.B::show();
在多层继承中,这种作用域运算符不允许嵌套使用
class D: protected C
{
···
};
int main(){
D d;
d.setA(100);
d.setB(200);
d.C::B::show(); //错误,直接d.B::show()即可
d.C::A::show(); // d.A::show()
}
除了使用d.B::show()
之外,还可以在D类中创建访问A类和B类成员x
的函数
void showA(){ cout<<A::x; }
void showB(){ cout<<B::x; }
由于C++通过作用域运算符解决二义性,因此特别规定任一基类在派生类中只能被继承一次
class E:protected A,protected A //错误
{
···
};
支配规则
在C++中,允许派生类中新增加的成员名与其基类的成员名相同,而且这种同名不产生二义性。在派生类中访问同名成员时,若直接用成员名访问,则访问的是派生类自身的成员,只有使用作用域运算符,才能访问同名基类成员。这种优先关系称为支配规则。
所以一个类出现同名函数有两种情况
- 多重继承下,来自不同的基类的同名函数
- 派生类定义了和某个来自基类的函数同名的函数
前者产生二义性,后者不产生
虚基类
在一些多重继承当中,会出现如下情况
class A{};
class B:protected A{};
class C:protected A{};
class D:protected B,protected C{};
类D会有两份类A的成员,而在多重继承中,欲使这类公共基类在派生类中只能有一个拷贝,可以将基类A说明成虚基类。说明的方法是在派生类继承基类时,在继承方式前或后加上关键字virtual
class B:virtual protected A{};
class C:protected virtual A{};
对于派生类D的构造函数,先调用虚基类的构造函数,再调用非虚基类的构造函数
对象成员
class A
{
int a;
public:
A(int n):a(n){}
void show(){ cout<<a<<endl; }
}
class B{
A a1; //对象成员
int b;
public:
B(int a,int n):a1(a) //这里只是类B的一个类A类型的成员变量a1
{ b = n;} //a1(a),对象a1调用构造函数进行初始化
void show(){ //而不是派生类的构造函数调用基类的构造函数
a1.show();
cout<<b;
}
}
赋值兼容
可以将公有派生类的对象赋值给基类对象,反之不可
还允许
- 只有公有继承的派生类对象可以赋值给基类对象
- 公有继承的派生类对象在赋值给基类对象时,系统将派生类对象中从基类继承来的成员赋值给基类对象
- 公有继承下,可以将派生类对象的地址赋给基类的指针变量,
Point *p = &line
- 公有继承下,派生类对象可以初始化基类的引用,
Point &p = line
第2点,也是基类不能赋值给派生类的解释,派生类是基类的扩充,基类赋值给派生类会导致派生类扩充部分无值可赋
最后两种情况,通过基类指针或引用,只能访问派生类对象中继承来的成员,无法访问派生类扩充的成员