继承分为:公有继承,保护继承,私有继承
用子类类型定义变量天经地义,class child : public parent这种格式就相当于把类继承之间的关系确定下来了,
继承的重要说明:
1,子类拥有父类的所有成员变量和成员函数。
2,子类可以拥有父类没有的方法和属性
3,子类对象可以当做父类对象使用
4,子类就是一种特殊的父类
一般情况下在项目开发中使用的是public继承。
派生类的访问一般受父类的定义权限的关键字和继承方式所影响。
public:修饰的成员变量,在类的内部、类的外部都能使用。
protected:修饰的成员变量方法,在类的内部使用,在继承的子类中可用。
private:修饰的成员变量方法,只能在类的内部使用,不能在类的外部。
1,C++中的继承方式会影响子类对外的访问属性:
public继承:父类成员在子类保持原有的访问级别
private继承:父类成员在子类中变为private成员
protected继承:父类中public成员会变成protected,父类中protected成员仍然为protected,父类中private成员仍然为private
2,private成员在子类中依然存在,但确是无法访问,不论哪种方式继承,派生类都不能直接使用继类的私有成员。
3,C++中子类对外访问属性表:
父类成员的访问级别 | ||||
继承方式 | public | protected | private | |
public | public | protected | private | |
protected | protected | protected | private | |
private | private | private | private |
4,三看原则
C++中的继承方式(public,private,protected)会影响子类的对外访问属性,判断某一句话能否被访问:
- 看调用语句,这句话写在子类的内部还是外部。
- 看子类如何从父类继承(public,private,protected)
- 看父类中的访问级别(public,private,protected)
5,类型兼容性原则
类型兼容性规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数,析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。类型兼容规则中所指的代替包括以下情况:
- 子类对象可以当做父类对象使用
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。
类型兼容规则是多态性的重要基础之一。
总结
:子类就是特殊的父类(base *p = &child;)
例子:
void howToPrint(Parent *base)
{
base . printP(); //父类的成员函数
}
void main()
{
Parent p1 ;
p1.printP() ;
child c1;
c1.printC() ;
c1.printP() ;
1,父类指针或者父类对象指向子类对象
赋值兼容性原则
1-1,基类指针 (引用) 指向 子类对象
Parent *p = NULL ; //定义指针 p
p = &c1 ; // c1是子类 的一个对象
p -> printP() ; // printP()是Parent 的一个函数
1-2,指针作函数参数
howToPrint (&p1) ; //父类的对象
howToPrint (&c1) ; //子类的对象(赋值兼容性原则),可以把子类对象的地址,仍给父类对象的指针
1-3,引用做函数参数
howToPrint (p1) ;
howToPrint (c1) ;
//base 就是c1的别名
2,可以让父类对象初始化子类对象
子类就是一种特殊的父类
Parent p3 = c1 ;
}
6,继承中的构造和析构
在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初始化。
在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清理。
自己新增加的,自己去初始化。
顺序:
构造: 先调用父类的构造函数对继承来的子类对象初始化,然后再调用自己构造函数对自己定义的对象初始化。
析构:先析构自己定义的对象,再去析构继承来的对象。
调用原则:
- 子类对象在创建时会首先调用父类的构造函数
- 父类构造函数执行结束后,执行子类的构造函数
- 当父类的构造函数有参数时,需要在子类的初始化列表中显示调用
- 析构函数调用的先后顺序与构造函数相反。
7、继承与组合混搭情况下,构造和析构调用原则
原则:
先构造父类,再构组合类,最后构造自己
先析构自己,再析构组合,最后析构父类对象
老祖宗的构造函数----->父亲的构造函数----->组合对象的构造函数----->自己的构造函数
析构的顺序相反
8,继承中的同名成员变量处理方法
- 当子类成员变量与父类成员变量同名时
- 子类依然从父类继承同名函数
- 在子类中通过作用域分辩符 :: 进行同名成员区分(在派生类中使用基类的同名成员,显示的使用类名限定符)
- 同名成员存储在内存中的不同位置
例子
class base
{
public:
int a , b ;
};
class derived : public base
{
public:
int b , c ;
};
void f()
{
derived d;
d . a = 1;
d . base :: b = 2; // 用基类的b
d . b = 3;//默认是子类的b
}
9,派生类中的static关键字
继承和 static 关键字在一起会产生什么现象?
- 基类定义的静态成员,将被所有派生类共享
- 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质(遵守派生类的访问控制)
- 派生类中访问静态成员,用以下形式显示说明----- 类名 :: 成员
或通过对象访问 对象名 . 成员
例子 (父类分配多少字节,子类继承也分多少字节)
class B
{
public:
static void add()
{i++;}
static int i;
void out()
{cout<<"static i="<<i<<endl;}
};
int B :: i = 0; //静态成员在类外初始化,并告诉编译器应该分配多少字节
//static关键字,遵守派生类的访问控制原则
//不是简单的变量赋值,更重要的是要告诉C++编译器,你要给我分配内存,
//我在继承中用到了a,不然会报错
class
D : private B
{
public:
void f();
{
i = 5;
add();
B :: i++;
B :: add();
}
} ;
10,多继承的派生类构造和访问
- 多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员
- 执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序
- 一个派生类对象 拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。
多继承语法:
class 派生类:访问控制 基类名1,访问控制 基类名2,.......访问控制 基类名n
{
数据成员和成员函数声明
};
class Derived : public Base1 , public Base2
11,虚继承-------主要用于多态
原因:如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
解决办法:应该在祖先类和父类之间加个virtual关键字。如下图: