继承与多态01 继承
继承与多态1
一.继承
C++是一种面向对象的语言,最重要的一个目的就是——提供可重用的代码,而类继承就是C++提供来扩展和修改类的方法。类继承就是从已有的类中派生出新的类,派生类继承了基类的特性,同时可以添加自己的特性。
继承本质:代码复用
形式为如下
类标识 类名:继承方式 基类名称
class People
{
public:
People(std::string name, bool sex = true) :mname(name), msex(sex){}
void Show()
{
std::cout << mname << std::endl;
}
static void Print()
{
std::cout << "hello world!" << std::endl;
}
public:
std::string mname;
bool msex;
//static int data;
};
//int People::data = 10;
class Student : public People
{
public:
Student(std::string name = "", bool sex = true ,std::string id = "") : People(name,sex) ,mid(id){}
private:
std::string mid;
};
继承方式有三种(public,protected,private)
1.派生类继承了基类的什么?
分为两个方面:1. 变量——派生类继承了基类中所有的成员变量,并从基类中继承了基类作用域,即使子类中的变量和父类中的同名,有了作用域,两者也不冲突。2.方法——派生类继承了基类中除去构造函数、析构函数以外的所有方法。
2.派生类中的内存布局
- 二.派生类对象的构造方式
前面也提到,派生类将基类中除去构造函数和析构函数的其他方法继承了过来,那么对于派生类对象中自己的成员变量和来自基类的成员变量,它们的构造方式是怎样的呢?
答案是:1.先调用基类构造函数,构造基类部分成员变量,再调用派生类构造函数构造派生类部分的成员变量。2.基类部分成员的初始化方式在派生类构造函数的初始化列表中指定。3.若基类中还有成员对象,则先调用成员对象的构造函数,再调用基类构造函数,最后是派生类构造函数。析构顺序和构造顺序相反。
- 系统先调动基类的构造函数
- 系统调动派生类的构造函数
并且在派生类的构造函数列表中表明基类的构造方式
派生类对象析构
- 系统调用派生类的析构
- 系统调用基类的析构
三.继承方式和访问限定符
继承方式有三种——public、protected和private,不同的继承方式对继承到派生类中的基类成员有什么影响?
Public:任意位置可以访问
Protected:本类类中和子类类中(派生类)访问
Private:本类类中访问
基类中不同访问限定符下的成员以不同继承方式继承后在派生类中的访问限定
基类成员(右) |
Public |
protected |
private |
public |
public |
protected |
不可访问 |
protected |
protected |
protected |
不可访问 |
private |
private |
private |
不可访问 |
(子类继承方式 上)
继承方式不会超过基类成员的大小(public最大就是public,protected最大就是protected,private最大就是private)
私有的原则 与继承方式无关 因此private都为不可访问
总的来说,父类成员的访问限定符通过继承派生到子类中之后,访问限定符的权限小于、等于原权限。其中,父类中的private成员只有父类本身及其友元可以访问,通过其他方式都不能进行访问,当然就包括继承。protected多用于继承当中,如果对父类成员的要求是——子类可访问而外部不可访问,则可以选择protected继承方式。
代码:
class Base
{
public:
int ma;//public成员
protected:
int mb;//protected成员
private:
int mc;//private成员
};
class Derived : private Base//private 继承
{
public:
void Show()
{
std::cout << mb << std::endl;
}
};
class Derived2 : public Derived//public 继承
{
public:
void Show()
{
//std::cout << mb << std::endl;
}
};
要看是不是protected,则看派生类的派生类中是否可见
要看是不是public,则看类外主函数是否可见
要看是不是private,则看能否在本类中访问
四、基类和派生类中同名成员的关系
派生类从基类中继承过来的成员(函数、变量)可能和派生类部分成员(函数、变量)重名。1.前面提到,派生类从基类中继承了基类作用域,所以同成员名变量可以靠作用域区分开(隐藏)。2.同名成员函数则有三种关系:重载、隐藏和覆盖。
1.基类和派生类中同名成员的关系
组合 a part of has_a
继承 a kindof is_a
代理
私有继承是has_a 公有,保护继承是is_a
2.同名函数
- 函数重载 overload
函数重载有三个条件,一函数名相同,二形参类型、个数、顺序不同,三相同作用域。根据第三个条件,可知函数重载只可能发生在一个类中,见下:
class Base
{
public:
Base(int a)
{
ma = a;
}
void show()
{
cout<<"base show 1"<<endl;
}
void show(int b)
{
cout<<"base show 2"<<endl;
}
private:
int ma;
};
其中,两个show函数构成函数重载。
- 函数隐藏 overhide 派生类中同名的方法隐藏了基类中所有同名方法
- 函数覆盖 override 重写
基类、派生类中的同名方法 函数头相同(参数、返回值),且基类中该方法为虚函数,则派生类中的同名方法将基类中方法覆盖。
允许基类指针或者引用指向或引用派生类对象
不允许派生类指针或引用指向或引用基类对象
如以下这个例子 :人不能干学生干的事 比如学生会解高数题 而随便一个人不会;
// 学生 = 人
Derived* pd = new Base(10);//派生类指针指向基类对象 错误
但是学生能干人干的事 例如 :吃饭睡觉
// 人 = 学生
Base* pb = new Derived(20);//基类指针指向派生类对象 正确
五、引用和指针
1. 基类对象和派生类对象
1.1 派生类对象可以赋值给基类对象
1.2 基类对象不可以赋值给基类对象
2. 基类指针(引用)和派生类指针(引用)
2.1 基类指针(引用)可以指向派生类对象,但只能访问派生类中基类部分的方法,不能访问派生类部分方法
2.2 派生类指针(引用)不可以指向基类对象,解引用可能出错,因为派生类的一些方法可能基类没有
编译器只支持从上到下的转换,即只能允许基类指针去指向派生类类对象。