继承与派生的基本概念
// 子类与父类的关系
- 子类对象占内存空间大,可以直接将子类对象赋值给父类对象:parent p = c;
- 子类对象占内存空间大,可以直接当做父类使用,只取其有用部分
- 父类指针可以指向子类对象。子类对象能满足父类指针的指向要求
- 子类指针不能直接指向父类指针,因为父类小、子类大,满足不了指向要求
// 父类和子类的构造过程
- 子类继承于父类,故子类中来自于父类的成员变量,应该调用父类中的构造函数初始化
- 子类并没有把父类的构造函数继承过来
- 当子类成员中新成员与从父类中继承过来的成员同名时,并不冲突
- 父类中被声明为static的成员,被父子类共享
// 查看类内布局
- cl /d1 reportSingleClassLayoutJava main.cpp
3种继承方式与访问权限
// 类成员访问属性说明
- private属性的成员:只能在本类中访问
- protected属性的成员,除了可以在本类中访问,还可以在子类中访问
- public属性成员,可以在任何地方访问
// 父类成员被子类继承后,这些成员在子类中的访问权限
- 继承自父类、继承后访问权限为public和protected的,均可被子类直接访问
- 继承自父类、继承后访问权限为private的成员,不可被子类直接访问
- 继承自父类、继承后访问权限为private的成员,只能被父类中原属性为public的函数访问
- 但是,子类中自身的private却可在子类类任意被访问
- // 此例子说明了private继承方式的作用
- // 将继承到了son这一代即可,到了孙子这一代,就不行了。
class parent
{
private:
int data;
public:
parent(int d = 0):data(d){}
void parent_print()
{
cout << data << endl;
}
};
class son : private parent // private继承将继承停在这一次继承,再次继承不可访问
{
public:
void son_print()
{
parent_print(); // 父类中public函数,哪怕被private继承,仍可在子类中访问
}
};
class sun :public son //
{
public:
void xprint()
{
parent_print(); // 父类中public函数被子类private继承后,无法被孙子类访问
son_print();
}
};
// 继承了什么?
- 父类的构造函数、析构函数不能被子类继承
- 其他所有成员变量、成员函数等,均被子类继承过来
-
// 同名函数
- 子类中的新函数与继承函数同名,新函数会屏蔽同名继承函数(指继承自父类的函数)
- son.myParent::print(); // 在子类中调用被屏蔽掉的同名继承函数
- 直接调用同名函数,实际上调用的是子类中新的同名函数,而非同名继承函数
// 编写子类的步骤
- 吸收父类的成员:除构造与析构后,其他所有成员均继承下来
- 改造父类成员:声明一个同名新成员,覆盖父类中的同名成员
- 发展新成员:编写与父类中不同名的新成员,发展新功能
- 编写新的构造函数与析构函数
// 构造子类对象的过程
- 先调用父类们的构造函数,按照它们在子类中继承的先后顺序来调用父类们构造函数
- 若继承的父类中有虚继承,构造的顺序是先虚继承,再普通继承
- 再调用成员对象的构造函数,按照成员在类中声明的先后顺序来调用这些成员的构造函数
- 最后才到子类的构造函数的相应构造操作,即函数体
赋值兼容原则
// 赋值兼容原则描述
- 子类对象可以当父类对象使用
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
// 赋值兼容原则详解
- 在需要使用父类对象的任何地方都可以使用子类对象来代替
- 赋值兼容原则是一种默认行为,不需要任何其他显式的转化步骤
- 将子类对象当父类使用,只能使用子类对象中从父类继承过来的成员,只能使用遗产
// 子类构造函数实现
-父类构造函数控制父类成员初始化,子类构造函数构造子类成员初始化
class parent
{
private:
int d1;
int d2;
public:
parent(int p1, int p2) :d1(p1), d2(p2)
{
cout << "父类构造函数" << endl;
}
~parent()
{
cout << "父类析构函数" << endl;
}
};
class child :public parent
{
private:
int cd;
public:
// 用父类对象在子类构造函数的初始化列表中完成子类构造函数
child(int p1, int p2, int c) :parent(p1, p2), cd(c)
{
cout << "子类构造函数" << endl;
}
~child()
{
cout << "子类析构函数" << endl;
}
};
// 既当父类,又当子类的情况
- 继承关系一直传递,构成一种继承链,最终派生类child包含所有基类的成员
class grandpa;
class parent : public grandpa;
class child : public parent;
// 不想当基类的类
class parent final; // parent就不能做其他类的基类
class parent final : public father; // parent就不能做其他类的基类