1、概念:
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
一个新类从已有的类中获得其已有的特性称为继承,被继承的称为父类(Base class)或基类,新产生的类称为派生类或子类。
2、继承的方式:
继承权限规则表
继承方式
基类的public成员
基类的protected成员
基类的private成员
继承引起的访问控制变化
Public
Public
Protected
不可见
基类成员在派生类中访问权限不变
Protected
Protected
Protected
不可见
基类的非私有成员成为子类的保护成员
Private
Private
Private
不可见
基类的所有成员成为子类的私有成员
class Base
{
public:
Base()
{
_pri = 1;
_pro = 2;
_pub = 3;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derived1 :public Base
{
public:
Derived1()
{
_d1_pri = 4;
_d1_pro = 5;
_d1_pub = 6;
}
void showd()
{
_pub = 0; // 仍为public成员
_pro = 2; // 仍为protected成员
//_pri = 3; // 父类私有成员对子类 不可见
}
private:
int _d1_pri;
protected:
int _d1_pro;
public:
int _d1_pub;
};
void TestPublic()
{
Base b;
b._pub = 1;
//b._pro = 2; // 父类对象不可访问父类保护成员
//b._pri = 3; // 父类对象不可访问父类私有成员
Derived1 d1;
d1._pub = 1; // 子类对象可以访问父类公有成员
//d1._pro = 2; //子类对象不可访问父类保护成员
//d1._pri = 3; // 子类对象不可访问父类私有成员
d1._d1_pub = 4;
}
// protected 保护继承
class Derived2 :protected Base
{
public:
Derived2()
{
_d2_pri = 4;
_d2_pro = 5;
_d2_pub = 6;
}
void showd()
{
_pub = 1; // 变为子类的protected成员
_pro = 2; // 仍为protected成员
//_pri = 3; // 父类私有成员对子类不可见
}
private:
int _d2_pri;
protected:
int _d2_pro;
public:
int _d2_pub;
};
void TestProtected()
{
Base b;
b._pub = 1;
//b._pro = 2; // 父类对象不可访问父类保护成员
//b._pri = 3; // 父类对象不可访问父类私有成员
Derived2 d2;
//d2._pub = 1; // 父类的public成员权限被修改为protected,子类对象不可访问
//d2._pro = 2; //子类对象不可访问父类保护成员
//d2._pri = 3; // 子类对象不可访问父类私有成员
d2._d2_pub = 4;
}
// private私有继承
class Derived3 :private Base
{
public:
Derived3()
{
_d3_pri = 4;
_d3_pro = 5;
_d3_pub = 6;
}
void showd()
{
_pub = 1; // 变为子类的privite成员
_pro = 2; // 变为子类的privite成员
//_pri = 3; // 父类私有成员对子类不可见
}
private:
int _d3_pri;
protected:
int _d3_pro;
public:
int _d3_pub;
};
void TestPrivate()
{
Base b;
b._pub = 1;
//b._pro = 2; // 父类对象不可访问父类保护成员
//b._pri = 3; // 父类对象不可访问父类私有成员
Derived3 d3;
//d3._pub = 1; // 父类的public成员权限被修改为private,子类对象不可访问
//d3._pro = 2; // 父类的protected成员权限被修改为private,子类对象不可访问
//d3._pri = 3; // 子类对象不可访问父类私有成员
d3._d3_pub = 4;
}
3、继承又可以分为单继承,多继承,钻石继承(又叫菱形继承),现在来谈论下三种情况下派生类的布局:
(1)单继承:
#include
using namespace std ;
class Base
{
public:
Base(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基类的构造函数" <
为啥子类对象赋值给父类对象可以,父类对象赋值给子类对象不可以
原因就是子类对象赋值给父类对象时,子类对象发生对象切片,将子类对象的父类部分赋值给父类对象,但是父类对象赋值给子类对象时,没有办法对子类自己的成员赋值,所以父类对象赋值给子类对象不可以。
(2)多继承
#include
using namespace std ;
class Base1
{
public:
Base1(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基类的构造函数" <从结果我们能分析,先调用base2的构造函数,在调用base1的构造函数,最后调用自己的构造函数,析构函数相反,这就说明先继承谁先调用谁的构造函数,子类的成员布局如下:
(3) 菱形继承:#include
using namespace std ;
class Base1
{
public:
Base1(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基类的构造函数" <
派生类Derived对象d成员布局如下:
从派生类对象成员布局可以看出,派生类对象继承了两份Base类成员,所以d._a访问不明确,因此虚继承的语法出现了。#include
using namespace std ;
class Base1
{
public:
Base1(int a= 0, int b= 1):_a(a),_b(b)
{
cout << "我是基类的构造函数" <
从结果分析我们能看出只是继承了一份基类,但是对象的大小变成了28,说明派生类中有8个字节是系统给我们自动加上的。
从图中我们可以看出确实只是继承了一份继承,但是基类的布局和原来的布局不一样,现在基类在最低下,Derived1中插入一个指针,指向一个表,表中存放了Deriverd1相对于基类的偏移量,Derived2也存放一个指针,同样指向一个表,这个表中同样存放了Derived2相对于基类的偏移量。从图中我们看出虚继承内存不一样的机制解决了变量的二义性问题。4.继承综合案例:#include
using namespace std ;
namespace
{
class Base
{
public:
Base(int a):_a(a)
{
cout << "我是基类的构造函数" <
5.继承注意点:(1)父类的构造、析构、拷贝构造、重载赋值运算符,友元函数不能继承。(2)基类没有缺省构造函数