继承的概念:
1. 继承机制是面向对象程序设计使代码复用(目的)的重要手段
2. 继承允许我们在保持原有类特性的基础上进行扩展,增加功能,在此基础上产生的类称作派生类。
3. 继承体现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
4. 继承是类层次的复用。
继承的语法注意事项:
1. 继承体系中,有三个访问限定符,分别为public, protected, private。
派生类对于基类成员的访问权限:
类成员 / 继承方式 | public继承 | protected继承 | private继承 |
基类的public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private继承 |
基类的protected成员 | 派生类的proteced成员 | 派生类的protected成员 | 派生类的private成员 |
基类的private成员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可见 |
2. 无论派生类以什么方式继承,基类的private成员都是不可见的。(基类的private成员被继承到了派生类对象中,但是派生类对象不管在类内还是类外都不可访问它)
3. protected继承出现的意义:基类成员不想在类外直接被访问,但是需要在派生类中能访问。
4. 使用关键字class的默认继承方式为private继承,使用struct时的默认继承方式为public 。在实际编写中,最好显式写出继承方式,增强代码可读性。
5. 实际运用中一般是使用public继承,扩展维护性较强。
6. 如何在派生类中访问基类的私有方法:构造基类的公有方法(这个公有方法可以访问基类的私有方法)
#include<iostream>
using namespace std;
class A
{
public:
void Test()
{ Print(); }
private:
void Print()
{ cout << "Private A" << endl; }
};
class B :public A
{};
int main()
{
B b;
b.Test();
//通过public继承A,使得B实例化的对象b能够访问Test(),进而访问A的私有函数Print()
}
7. 基类会包含父类的大小,且存在字节对齐。
基类和派生类对象赋值转换(赋值兼容规则)
1. 派生类对象可以赋值给 基类的对象 / 基类的指针 / 基类的引用。
2. 派生类给父类赋值只是赋值给数据成员。
3. 基类对象不能赋值给 派生类对象。
4. 基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指向派生类对象时才是安全的。(不安全(越界访问)情况如下)
#include<iostream>
using namespace std;
class Person
{
protected:
string name;
string sex;
};
class Student:public Person
{
protected:
int number;
public:
void Number(int n)
{ number = n; }
void Print()
{ cout << number << endl; }
};
int main()
{
Student A; A.Number(5);
Person* p = &A;
Student* s = (Student*)p;
s->Print();
return 0;
}
这类情况下,基类的指针通过强制类型转换赋值给了派生类的指针,此时虽然可以访问number,但存在越界访问的问题,有安全隐患。
继承中的作用域
1. 在继承体系中,基类和派生类都有独立的作用域
2. 基类和派生类中有同名成员,派生类将屏蔽对基类的同名成员的直接访问,这个情况叫做同名隐藏,也叫重定义。(在子类成员函数中,可以使用基类::基类成员 显示访问)
3. 如果是成员函数的隐藏,只需函数名相同就构成隐藏。
派生类的默认成员函数
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构
继承与友元
1. 友元关系不能继承,也就是说基类的友元函数不能访问派生类的私有和保护成员。
继承与静态成员
1. 基类定义了static静态成员,那么无论派生出多少个子类,整个继承体系里面只有一个这样的成员。
菱形继承
1. 菱形继承具有数据冗余和二义性问题。
2. 虚拟继承可以解决菱形继承的二义性和数据冗余问题
继承的其他问题
1. public继承是一种is - a问题 ,即每个派生类对象都是一个基类对象。(人和狗都是动物)
2. 组合是一种has - a问题,即B组合了A,每个B对象中都有一个A对象。(轮子和车)
3. 根据高内聚,低耦合原理,优先使用对象组合,而非类继承。
4. 通过生成派生类的复用通常被称为白箱复用,通过对象组合的复用被称为黑箱复用。