hello,这里是bangbang,今天来讲下继承。
面向对象三大特性:封装、继承、多态。
目录
1. 继承的概念及定义
1.1 继承的概念
1.2 继承定义
1.2.1 定义格式
1.2.2 继承关系和访问限定符
1.2.3 继承基类成员访问方式的变化
2. 基类和派生类对象赋值转换
3. 继承中的作用域
4. 派生类的默认成员函数
5. 继承与友元与静态成员
5.1 继承与友元
5.2继承与静态成员
6. 继承模型
6.1 单继承
6.2 多继承
6.3 菱形继承
6.3.1 菱形虚拟继承
6.3.2 菱形虚拟继承原理
7. 继承总结
Person是父类,也称作基类。Student是子类,也称作派生类。
类成员/继承方式
|
public继承
|
protected继承 | private继承 |
基类的public成员
|
派生类的public成员
|
派生类的protected成员
|
派生类的private成员
|
基类的protected成员
|
派生类的protected成员
|
派生类的protected成员
|
派生类的private成员
|
基类的private成员
|
在派生类中不可见
|
在派生类中不可见
|
在派生类中不可见
|
题外话:我们可以看到基类的私有成员无论继承方式如何都是不可见的,其实这是C++大佬们踩的坑点,大佬在设计的时候想把所有情况都包含进去,但实际情况上大多数项目都使用的是public很少有private,但是因为语言的向上发展性,大佬们几乎不可能把语言回退(除了python3不兼容python2)避开坑点,只能再想办法填补这个坑,我们虽然知道这是坑点,但也要学习。
总结:
题目:
如何定义一个不能被继承的类?
答:
C++98:父亲构造函数私有——子类是不可见。子类对象实例化,无法调用构造函数。
C++11:新增加关键字final(最终类)
1.子类对象可以直接赋值给父类对象/指针、引用。
Student sobj ; // 1.子类对象可以赋值给父类对象/指针/引用 Person pobj = sobj ; Person* pp = &sobj; Person& rp = sobj;
2.基类对象不能赋值给派生类对象。
//2.基类对象不能赋值给派生类对象 sobj = pobj;//错误
3.基类的指针可以通过强制类型转换赋值给派生类的指针
pp = &sobj Student* ps1 = (Student*)pp; // 这种情况转换时可以的。 ps1->_No = 10; pp = &pobj; Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题 ps2->_No = 10;
子类自己的成员跟类一样。(内置类型不处理->随机值,给缺省值,缺省值处理;自定义类型调用它的处理)
继承父类成员,必须调用父类的构造函数。如果基类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显示调用。
不允许子类在初始化列表直接初始化父类成员变量,采取匿名对象在初始化列表初始。
class Person
{
public:
Person(const char* name)
: _name(name)
{
cout << "Person()" << endl;
}
protected:
string _name;
}
class Student:public Person
{
public:
Student(const char* name, int num)
:Person(name) //匿名对象在初始化列表初始
, _num(num)
{
cout << "Student()" << endl;
}
protected:
int _num;
}
同上
不用显示调用父类析构函数,自动调用。 先析构子类再析构父类。
自己成员,跟类和对象一样。(内置类型指拷贝->浅拷贝,自定义类型调用它的拷贝构造)
继承的父类成员,必须调用父类拷贝构造初始化。
同上
operator=必须要调用父类的operator=完成父类的复制。
题目:
多继承:(对象分布)先继承的在前面,后继承的在后面。
可以看到p1和p3实际上指向同一地方。
成员变量是局部变量放在栈中,入元素放栈顶,地址逐渐增高,b2是后继承的,所以地址比b1高,也就是p2比p1高。
这里给出一个简化的菱形继承模型用来研究。
class A
{
public:
int _a;
};
//class B : public A
class B : virtual public A
{
public:
int _b;
};
//class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._a = 0;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
存储情况符合我们上面给出的菱形继承对象模型。
我们可以看到D对象中将A放到了对象组成的最下面,这个A同时属于B和C(公共的A)原来B和C存放A的位置放了2个地址,这2个地址(也就是虚基表指针)指向了一张表(虚基表),虚基表中存的是偏移量,通过偏移量找到公共的A。
3.继承和组合