目录
一.相关概念
二.派生类的相关注意事项
多层继承关系
成员对象和多层继承的区别
四.赋值兼容规则
五.继承关系中的构造函数和析构函数
一.相关概念
基类(父类):被继承的类
派生类(子类):新产生的类
派生反映的是事物之间的联系,如学生是人,大学生是学生等。继承是类型设计层面上的复用
二.派生类的相关注意事项
1.派生类除了构造函数与析构函数,拷贝构造函数,赋值重载函数外不论是数据成员还是函数成员均能继承。
2.如果基类中的成员和派生类成员重名时,会产生同名隐藏,派生类中的优先,会隐藏基类中的成员
3.派生类中的析构函数和构造函数需要重写
三.不同继承方式的区别
公有继承,保护继承,私有继承
多层继承关系
class father {
private:
int a;
protected:
int b;
public:
int c;
};
class son :public father {
private:
int d;
protected:
int e;
public:
int f;
};
int main() {
father f;
son s;
return 0;
}
通过调试我们可以发现基类对象继承了父类中的所有(除构造函数和析构函数外)的,不管你是私有的还是被保护的都能继承下来。 并且我们也能知道基类是如何构造的
在类型的继承层次中,保护属性当做公有属性
继承下来的对象是不具名对象,由父类名字所管辖。先去构建父类中继承下来的,然后再是自己派生出来的新对象。
class father {
private:
int a;
protected:
int b;
public:
int c;
};
class son :public father {
private:
int d;
protected:
int e;
public:
int f;
void fun() {
d = e = f = 10;
b = 20;
c = 30;
}
};
int main() {
father f;
son s;
s.fun();
return 0;
}
通过fun()这个函数我们可以改变派生类中的对象和基类中被保护和公有的对象,为什么不能改变父类中的私有对象,因为私有对象的可操作域只在该类中,子类不在父类中因此无法更改,而我们的fun()在子类中,所以改变子类的私有对象
如果我们把公有继承改成私有继承,仍是可以改变父类的保护属性和公有属性,不能改变父类的私有属性
class son :private father {
private:
int d;
protected:
int e;
public:
int f;
void fun() {
d = e = f = 10;
b = 20;
c = 30;
}
};
为什么呢?
可以这么理解:father和d都是私有属性,既然d都能访问,为什么不能访问father呢,至于father中的私有属性,那是因为他的可见域只在father类中,所以无法更改
成员对象和多层继承的区别
class son {
private:
int d;
protected:
int e;
public:
father fa;
int f;
void fun() {
d = e = f = 10;
fa.c = 20;
}
};
这是成员对象的基类的结构图
成员对象与多层继承最大的区别就在于被保护的属性,成员对象是不能访问其保护对象,而多层是可以的。
四.赋值兼容规则
- 派生类的对象可以赋值给基类对象。
- 派生类的对象可以初始化基类的引用。
- 派生类对象的地址可以赋给指向基类的指针。
son中的不具名对象和father中的值是一样的,所以可以将基类的值赋值给父类,这时会出现切片现象,而不能将父类赋值给基类
逻辑:学生一定是人,但是人不一定是学生
物理:father的空间是小于son的,如果将father赋给son,则会导致son中的值并没有存完,因此出现错误。
五.继承关系中的构造函数和析构函数
class Person {
private:
int _id;
public:
Person() :_id(0) {
cout << "Create Person()" << endl;
}
Person(int id) :_id(id) {
cout << "Create Person(int)" << endl;
}
~Person() {
cout << "Destroy Person" << endl;
}
};
class Trans {
private:
int cnum;
public:
Trans(int c = 0) :cnum(c) {
cout << "Create Trans" << endl;
}
~Trans() {
cout << "Destory Trans" << endl;
}
void PrintTrans()const {
cout << "cnum: " << cnum << endl;
}
};
class Student :public Person {
private:
int _sid;
Trans table;
public:
Student(int id, int sid, int n) :Person(id), _sid(sid), table(n) {
cout << "create Student" << endl;
}
~Student() {
cout << "Destroy Student" << endl;
}
};
int main() {
Student s1(2020,2,4);
}
构造函数:先构造父类再按照派生类中的顺序一次构建:即构建课程表类最后构建学生类型。
析构的时候:有类似退栈,先析构最上边的学生类,再析构课程表类,最后析构人类
在Student的构造函数时注意一个大问题:
Student(int id, int sid, int n) : _sid(sid), table(n) {
cout << "create Student" << endl;
}
如果这里没有Person的有参构造,不管是无参构造或者是不写都会调用Person的无参构造,因此为了避免这类情况出现,我们必须在构造函数中明确说明将id的值调用Person的有参构造。
下一篇文章将分析拷贝构造函数和赋值重载,以及友元,静态成员等相关内容。