C++继承
目录
[TOC]
一句话说清继承(inheritance)
子承父业。继承==派生。子类是父类的派生(Derive),子类继承父类的成员变量和成员函数。
三种继承方式
public、protected、private:限定了最高访问权限。
※三个关键字的访问范围
类成员函数 | 子类函数 | 友元 | 本身的类的对象 | |
---|---|---|---|---|
Public | √ | √ | √ | √ |
Protected | √ | √ | √ | × |
Private | √ | × | × | × |
不论是哪一种继承,父类中的private
成员都无法在子类中使用。而且大部分时候为了简单,我们使用public
继承。
继承时的构造函数和析构函数
构造函数
我们知道,private
成员虽然存在于子类,但是既不可见也不可用,也无法使用using
关键字更改权限。那么我们子类在写构造函数时又该怎么办呢?
答案是:调用父类的构造函数。
class People{
private:
char *m_name;
int m_age;
public:
People(char *name,int age):m_name(name),m_age(age){};
}
class Student : public People{
private:
int m_score;
public:
Student(char *name, int age, int score):People(name,age),m_score(score){};
}
值得注意的是,People(name,age) 这一段调用父类构造函数,只能写在初始化列表里,而不能写在函数体内部。
同时,这也导出了构造函数的调用顺序,即自顶向下,先调用父类再调用子类。但是,这里需要注意一点,即C++中子类只能调用直接父类的构造函数,不能调用间接父类的。
析构函数
析构函数和构造函数的执行顺序恰好相反。且析构函数不重载。
多重继承和虚继承
多重继承
一句话:一个儿子有多个父亲。
这好吗?这不好。所以Java,C#等都取消了多继承。
多继承其实和单继承区别不大,如果出现了命名冲突,使用域解析符::
即可。
虚继承
这玩意是为了解决多继承会带来的命名冲突和冗余数据的问题而应运而生的。
※一定要知道虚继承和虚函数是两码事 虽然都是virtual关键字。虚继承是为了给多重继承打补丁,而虚函数是为了实现运行时的多态。
无论虚基类在继承体系中出现了多少次,在最后那个子类中,都只包含一份虚基类的成员。
一个很典型的例子就是STL中的iostream类,其父类istream和ostream均继承自base_ios,于是在声明时,
class istream : virtual public base_ios{};
class ostream : virtual public base_ios{};
class iostream :public istream, public ostream{};
向下转型和向上转型
一句话理解转型
类也是一种数据类型,在子类和父类之间可以发生“类型转换”。把子类的值、指针、引用赋值给父类,这叫向上转型,反之则被称为向下转型。
向上转型是安全的,因为子类比父类,东西只会多不会少。
class A{
public:
void show(){cout<<a<<endl;}
private:
int a;
}
class B :public A{
public :
void show(){cout<<a<<b<<endl;}
private :
int b;
}
向上转型
A a(10);
B b(12,13);
a.show();b.show();
a = b;//向上转型
a.show();b.show();
最后show出来的a不等于10,而是等于12。这是把子类的值赋给父类。
而对象指针之间的赋值则有所不同,指针的赋值并不拷贝对象的成员,而仅仅只是改变了指针的指向。
- 编译器通过指针访问成员变量时,指针指向哪个对象就用哪个对象的数据
- 编译器通过指针访问成员函数时,指针属于哪个类的类型,就访问哪个类的函数。
而引用和指针类似,可以由父类的引用指向子类的对象。
向下转型
这就涉及到C++四种类型转换运算符之中的dynamic_cast
了。
语法dynamic_cast <newType> (expression)
其中newType
和expression
必须同时为指针or引用。在向下转型时借助RTTI进行检测,对于指针,转换失败抛出NULL
; 对于引用,转换失败抛出std::bad_cast
。
简单来说,向下转型是不安全的,非必要不使用吧。