• C++面向对象程序设计 - 派生类的构造函数和析构函数


            构造函数的主要作用对数据成员初始化,基类的构造函数是不能被继承的,在声明派生类时,派生类并没有把类的构造函数继承下来。因此,对继承过来的基类成员初始化的工作也要由派生类的构造函数完成;所以在派生类中不仅要考虑自身增加的数据成员的初始化,还要考虑基类的数据成员的初始化。

    一、派生类的构造函数

            解决方法则是,在执行派生类的构造函数时,调用基类的构造函数。

            其一般形式为

    派生类构造函数名 ( 总参数表列 ) : 基类构造函数名 ( 参数表列 ) 

    {

            派生类中新增数据成员初始化语句

    }

            这里通过人与学生类示例来演示,将Person类数据成员设置为保护成员,这样方便直接在派生类中调用。代码如下:

    1. #include
    2. #include
    3. using namespace std;
    4. // 基类 - 人类
    5. class Person{
    6. protected:
    7. string name; // 姓名
    8. int age; //年龄
    9. char gender; //性别
    10. public:
    11. Person(){
    12. name = "anonym";
    13. age = 0;
    14. gender = 0;
    15. }
    16. Person(string name, int age, char gender): name(name), age(age), gender(gender){}
    17. };
    18. // 派生类 - 学生
    19. class Student: public Person{
    20. private:
    21. string school;
    22. public:
    23. Student(string name, int age, char gender, string school): Person(name, age, gender){
    24. this->school = school;
    25. }
    26. void display(){
    27. cout <<"name:" <
    28. cout <<"age:" <
    29. cout <<"gender:" <<(gender=='1'?"male":"female") <
    30. cout <<"school:" <
    31. }
    32. };
    33. int main(){
    34. Student s("Tom", 18, '1', "middle school");
    35. s.display();
    36. return 0;
    37. }

            运行结果如下:

    在建立一个对象时,执行构造函数的顺序:

    1. 派生类构造函数先调用基类构造函数;
    2. 再执行派生类构造函数本身(即派生类构造函数的函数体)。

    二、有子对象的派生类的构造函数

            在类对象中,数据成员都是标准类型(如int,char)或系统提供的类型(如string),但实际上,类的数据成员中还可以包含类对象。类对象中内嵌对象,即对象中的对象,称为子对象。

            这里将学生对应的班长,先以Person类表示,在Student类中定义子对象并在派生类构造函数中初始化。代码如下:

    1. #include
    2. #include
    3. using namespace std;
    4. // 基类 - 人类
    5. class Person{
    6. protected:
    7. string name; // 姓名
    8. int age; //年龄
    9. char gender; //性别
    10. public:
    11. Person(){
    12. name = "anonym";
    13. age = 0;
    14. gender = 0;
    15. }
    16. Person(string name, int age, char gender): name(name), age(age), gender(gender){}
    17. void display(){
    18. cout <<"name:" <
    19. cout <<"age:" <
    20. cout <<"gender:" <<(gender=='1'?"male":"female") <
    21. }
    22. };
    23. // 派生类 - 学生
    24. class Student: public Person{
    25. private:
    26. Person monitor; // 定义子对象(班长)
    27. string school;
    28. public:
    29. // 构造函数,并初始化基类和子对象
    30. Student(string name, int age, char gender, string school, string m_name, int m_age, char m_gender):
    31. Person(name, age, gender), school(school), monitor(m_name, m_age, m_gender){}
    32. void display(){
    33. cout <<"name:" <
    34. cout <<"age:" <
    35. cout <<"gender:" <<(gender=='1'?"male":"female") <
    36. cout <<"school:" <
    37. cout <"monitor:" <
    38. monitor.display();
    39. }
    40. };
    41. int main(){
    42. Student s("Tom", 18, '1', "middle school", "John", 19, '1');
    43. s.display();
    44. return 0;
    45. }

            运行结果如下:

            派生类构造函数的任务包括三部分:

    1. 对基类数据成员初始化;
    2. 对子对象数据成员初始化;
    3. 对派生类数据成员初始化。

            定义派生类构造函数的一般形式:

    派生类构造函数名 (总参数表列) : 基类构造函数名 (参数表列), 子对象名 (参数表列)

    {

            派生类中新增数据成员初始化语句

    }

            执行派生类构造函数的顺序:

    1. 调用基类构造函数,对基类数据成员初始化;
    2. 调用子对象构造函数,对子对象数据成员初始化;
    3. 再执行派生类构造函数本身,对派生类数据成员初始化。

    三、多层派生时的构造函数

            一个类不仅可以派生出一个派生类,派生类还可以继续派生,形成派生的层次结构。

            这里在Person类和Student类基础上再定义一个Teacher类,其顺序是Person -> Student -> Teacher。示例代码如下:

    1. #include
    2. #include
    3. using namespace std;
    4. // 基类 - 人类
    5. class Person{
    6. protected:
    7. string name; // 姓名
    8. int age; //年龄
    9. char gender; //性别
    10. public:
    11. Person(){
    12. name = "anonym";
    13. age = 0;
    14. gender = 0;
    15. }
    16. Person(string name, int age, char gender): name(name), age(age), gender(gender){}
    17. };
    18. // 派生类 - 学生
    19. class Student: public Person{
    20. private:
    21. string school;
    22. public:
    23. // 构造函数,并初始化基类
    24. Student(string name, int age, char gender, string school):
    25. Person(name, age, gender), school(school){}
    26. string show_school(){
    27. return school;
    28. }
    29. void display(){
    30. cout <<"name:" <
    31. cout <<"age:" <
    32. cout <<"gender:" <<(gender=='1'?"male":"female") <
    33. cout <<"school:" <
    34. cout <
    35. }
    36. };
    37. class Teacher: public Student{
    38. private:
    39. string course; //课程
    40. public:
    41. // 构造函数,并初始化基类
    42. Teacher(string name, int age, char gender, string school, string course):
    43. Student(name, age, gender, school), course(course){}
    44. void display(){
    45. cout <<"name:" <
    46. cout <<"age:" <
    47. cout <<"gender:" <<(gender=='1'?"male":"female") <
    48. cout <<"school:" <<show_school() <
    49. cout <<"course:" <
    50. cout <
    51. }
    52. };
    53. int main(){
    54. Student s("Tom", 18, '1', "middle school"); //定义学生类对象
    55. Teacher t("Lily", 30, '2', "middle school", "Mathematics"); //定义教师类对象
    56. s.display(); // 显示学生信息
    57. t.display(); //显示教师信息
    58. return 0;
    59. }

            运行结果如下:

            初始化顺序:

    1. 先初始化基类(Person)的数据成员;
    2. 再初始化派生类(Student)的数据成员;
    3. 最后再初始化派生类(Teacher)的数据成员。

    四、派生类的析构函数

            析构函数的作用是在对象撤销之前,进行必要的清理工作。当对象被删除时,系统会自动调用析构函数。

            在派生类中析构函数不能被继承,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义析构函数,用来对派生类中所增加的成员进行清理工作,基类的清理工作扔然由基类的析构函数完成。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。

            析构函数的调用顺序刚好与构造函数相反,析构函数是先调用派生类的析构函数,对派生类的成员进行清理,然后调用子对象的析构函数对子对象进行清理,最后调用基类的析构函数对基类进行清理。

            这里将子对象中代码稍作修改,来演示析构函数执行顺序,示例代码如下:

    1. #include
    2. #include
    3. using namespace std;
    4. // 基类 - 人类
    5. class Person{
    6. protected:
    7. string name; // 姓名
    8. int age; //年龄
    9. char gender; //性别
    10. public:
    11. Person(){
    12. name = "anonym";
    13. age = 0;
    14. gender = 0;
    15. }
    16. Person(string name, int age, char gender): name(name), age(age), gender(gender){}
    17. // 基类的析构函数
    18. ~Person(){
    19. cout <", Person constructor" <
    20. }
    21. };
    22. // 派生类 - 学生
    23. class Student: public Person{
    24. private:
    25. Person monitor; // 定义子对象(班长)
    26. string school;
    27. public:
    28. // 构造函数,并初始化基类和子对象
    29. Student(string name, int age, char gender, string school, string m_name, int m_age, char m_gender):
    30. Person(name, age, gender), school(school), monitor(m_name, m_age, m_gender){}
    31. // 派生类的析构函数
    32. ~Student(){
    33. cout <", Student constructor" <
    34. }
    35. };
    36. int main(){
    37. Student s("Tom", 18, '1', "middle school", "John", 19, '1');
    38. return 0;
    39. }

            运行结果如下:

            由图可见,基类(Person类)是最后执行的,子对象(John对象)是在基类之前执行,而最先执行的则是派生类(Student类)。

            这个过程是确保了资源在正确的顺序中被释放。

  • 相关阅读:
    戴哥说Java(三)——Java的反射和注解
    如何进行任务优先级的确定
    聊聊操作系统中 进程 and 线程中哪些事??
    MySql--表的基本查询(CRUD)
    树与图的广度优先遍历
    Metabase:简单快捷的商业智能与数据分析工具 | 开源日报 No.61
    计算机管理服务中找不到mysql的服务
    TypeScript 高级类型-详解
    基于Java+SpringBoot+Mybatis+Vue+ElementUi的影视管理系统
    多进程间通信学习之有名管道
  • 原文地址:https://blog.csdn.net/jiciqiang/article/details/138219779