1.复习6个默认成员函数
默认:我们不写,编译器会自动帮我们生成(重点看前4个)
- 构造函数:完成初始化工作
- 析构函数:完成清理工作
- 拷贝构造:使用同类对象初始化创建对象
- 赋值重载:把一个对象赋值给另一个对象
- 普通对象取地址重载
- const对象取地址重载
C++ 中派生类(子类)拷贝构造函数的实现方式:派生类的拷贝构造函数需要显式调用父类的拷贝构造函数来初始化继承的成员,同时自己初始化派生类新增的成员。这样才能保证整个对象(包括父类部分和子类新增部分)被正确拷贝。
拷贝构造函数
Person(const Person& p)//Person的拷贝构造函数
: _name(p._name)//初始化类的成员变量
{
cout << "Person(const Person& p)" << endl;
}
//Student 是 Person 的派生类(子类),因此 Student 的对象包含 Person 部分的成员(继承自父类的
//成员)和自己的成员。
Student(const Student& st)//Student的拷贝构造函数
:Person(st)//这里有点不懂,解释如下
,_x(st._x)
, _address(st._address)
{}
Person(st):需要先把父类中继承过来的对象进行初始化
构造函数:需要先父后子,因为子类成员可能会用到父类成员,如果父类成员没有先初始化,那么父类成员就是随机值
析构函数:(会自动调用)需要先子后父,因为子类析构是可能用到父类成员的,如果先父后子就可能出问题
2.继承和友元
友元关系不能继承(即基类友元不能访问子类被保护成员和私有成员)

复习友元函数概念:打破类的访问权限限制,使得外部函数可以直接访问类的受保护或私有成员,用于便捷地展示或操作类的内部数据
如上图所示,Display()函数是父类Person的友元,能够在类外访问父类的protected对象_name;而不能访问子类的preotected成员_stuNum
3.继承和静态成员
若静态成员在父类中被定义,则在整个继承体系中有且只有一个static静态成员实例
class Person
{
public:
Person() { ++_count; }
protected:
string _name; // 姓名
public:
static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum; // 学号
};
int main()
{
Person p;
Student s;
cout << &(Person::_count) << endl;
cout << &(Student::_count) << endl;
return 0;
}

如上图所示,Student继承了父类的静态成员_count,则子类与父类的_count是同一个,地址相同。
4.复杂的菱形继承
单继承:一个子类只有一个直接父类
多继承:一个子类有两个或以上直接父类(一个类型具有两个类型的特征)
菱形继承:如下面的例子,冬虫夏草只有一个_life属性,因此会产生数据冗余和二义性的问题
对于二义性问题,我们可以采用Animal::_life,Plant::_life来解决它到底说的哪一个
对于数据冗余问题,我们采用虚拟菱形继承(日后专门写一篇博客)

5.继承和组合
两者本质都是类的复用,建议优先使用组合
继承:“是”的关系,比如猫是动物,继承动物会呼吸的特性,除此之外还有抓老鼠的本领;部分变了,继承的东西也得跟着变,比如动物不会呼吸了,那猫也不会呼吸了。
组合:“有”的关系,比如汽车里有发动机,是部分和整体的关系;部分变了,整体可能不变,比如换个发动机,汽车还是汽车。(耦合度低,代码维护性好)
继承(白箱复用):就好比你拿到一台旧机器(父类),不仅能用它的功能,还能拆开看内部结构(父类的实现细节),甚至修改里面的零件(重写父类方法),再把它改造成新机器(子类)。
组合(黑箱复用):就好比你买了一个封装好的工具箱(其他类),你只需要知道怎么用它的按钮(公开接口),不用管里面的零件是怎么拼的。你把这个工具箱放进自己的柜子(当前类)里,通过按钮调用它的功能。
3483

被折叠的 条评论
为什么被折叠?



