C++ 继承(1): 继承方式(public, protected, private)

C++ 继承(1): 继承方式(public, protected, private), 继承中的特殊关系(隐藏 , is-a) - 在水一方xym的博客 - CSDN博客

C++远征之继承篇 视频教程 笔记 方便自己查阅和复习,温故而知新。

 

目录

1 c++ 继承简介

代码示例

2 继承方式

总结

3 继承中的特殊关系

3.1 隐藏

代码示例

3.2 is-a

代码示例

4 多继承与多重继承

5 虚继承

参考资料


 

1 c++ 继承简介

通常,类库是以源代码的方式提供的,这意味着可以对其进行修改,以满足要求。然而,C++提供了比修改代码更好的方法来拓展和修改类,这种方法称之为 类继承,它能够从已有的类派生出新的类,而派生类继承原有类的特征,包括方法。 例如继承一笔财产要比自己白手起家容易,通过继承派生出的类通常比设计新的类容易得多。

但是生活中的继承 和 C++中的继承不能等同。

 

那么为什么要继承呢? 下面举一个例子,比较这两种类,Worker类用到了Person类的数据成员以及成员函数,如果不去重新定义,而是直接拿来用,那将会很方便。

 

所以 我们可以 在Worker中 将Person继承过来,形式如下:

 

下面介绍一下类继承所用到的专有名词:

 

那么在内存中,子类父类 的关系 如下图所示:

 

 

代码示例

要求:查看 父类 和子类构造函数 执行地先后顺序

1 定义Person类,要求含有m_strNmae 和 m_iAge两个数据成员及构造函数 析构函数 eat函数

2 定义Worker类,要求共有继承Person类,含有数据成员m_iSalary , 构造函数 析构函数 work函数

 


 
 
  1. //Person.h
  2. #pragma once
  3. #include<iostream>
  4. #include<string>
  5. using namespace std;
  6. class Person
  7. {
  8. public:
  9. Person(); //构造函数
  10. ~Person(); //析构函数
  11. void eat();
  12. string m_strName;
  13. int m_iAge;
  14. };

 
 
  1. //Person.cpp
  2. #include<iostream>
  3. #include"Person.h"
  4. using namespace std;
  5. Person::Person()
  6. {
  7. cout << "Person() 构造" << endl;
  8. }
  9. Person::~Person()
  10. {
  11. cout << "~Person() 析构" << endl;
  12. }
  13. void Person::eat()
  14. {
  15. cout << "Person eat()" << endl;
  16. }

 
 
  1. //Worker.h
  2. #pragma once
  3. #include<iostream>
  4. #include"Person.h"
  5. using namespace std;
  6. class Worker: public Person //公有继承
  7. {
  8. public:
  9. Worker();
  10. ~Worker();
  11. void work();
  12. int m_iSalary;
  13. };

 
 
  1. //Worker.cpp
  2. #include<iostream>
  3. #include"Worker.h"
  4. using namespace std;
  5. Worker::Worker()
  6. {
  7. cout << "Worker() 构造" << endl;
  8. }
  9. Worker::~Worker()
  10. {
  11. cout << "~Worker() 析构" << endl;
  12. }
  13. void Worker::work()
  14. {
  15. cout << "Worker work()" << endl;
  16. }

 
 
  1. //main.cpp
  2. #include<iostream>
  3. #include"Worker.h"
  4. using namespace std;
  5. /*************************
  6. 类继承
  7. 要求:查看 父类 和子类 构造函数 执行地先后顺序
  8. 1 定义Person类,要求含有m_strNmae 和 m_iAge两个数据成员 及 构造函数 析构函数 eat函数
  9. 2 定义Worker类,要求共有继承Person类,含有数据成员m_iSalary , 构造函数 析构函数 work函数
  10. **************************/
  11. int main()
  12. {
  13. Worker *p = new Worker;
  14. p->m_strName = "SB";
  15. p->m_iAge = 3;
  16. p->eat();
  17. cout << "-------------------" << endl;
  18. p->m_iSalary = 10000;
  19. p->work();
  20. delete p;
  21. p = NULL;
  22. cin.get();
  23. return 0;
  24. }

运行结果:

 

:由实验结果可知:

构造函数: 先执行父类的构造函数 后执行子类的构造函数;

析构函数:先执行子类的析构函数 后执行父类的析构函数。

 

 

2 继承方式

类继承 有三种方式:

 

例如,公有继承 如下:

 

公有继承的访问:

 

那么 对于 protected 只能继承 不能访问,如下图所示:

 

 

总结

public 继承:

 

protected 继承:

 

private 继承:

 

 

3 继承中的特殊关系

3.1 隐藏

继承中的特殊关系,子类的数据成员或成员函数 与 父类的数据成员或成员函数 同名,此时,父类的称之为:隐藏

 

这种隐藏 可以 概括为: 父子关系 成员同名 隐藏

那么 如何访问 子类和父类的 同名函数呢? 下面举一个例子来说明:

 

 

对于 子类和父类的 数据成员同名 也同样举一个例子,如下图所示:

 

 

 

代码示例

继承关系中的 隐藏
要求:
1 定义Person类
    数据成员: m_strName 
    成员函数:构造函数, play()

2 定义Soldier类:
    数据成员: 无
    成员函数:构造函数, play() worker()

 


 
 
  1. //Person.h
  2. #pragma once
  3. #include<iostream>
  4. #include<string>
  5. using namespace std;
  6. class Person
  7. {
  8. public:
  9. Person(); //构造函数
  10. ~Person(); //析构函数
  11. void play();
  12. string m_strName;
  13. int m_iAge;
  14. };

 
 
  1. //Person.cpp
  2. #include<iostream>
  3. #include"Person.h"
  4. using namespace std;
  5. Person::Person()
  6. {
  7. cout << "Person() 构造" << endl;
  8. }
  9. Person::~Person()
  10. {
  11. cout << "~Person()--析构" << endl;
  12. }
  13. void Person::play()
  14. {
  15. cout << "Person--play()" << endl;
  16. }

 


 
 
  1. //Soldier.h
  2. #pragma once
  3. #include<iostream>
  4. #include"Person.h"
  5. class Soldier: public Person
  6. {
  7. public:
  8. Soldier();
  9. void play(); //与父类中的函数同名
  10. void work();
  11. protected:
  12. };

 
 
  1. //Soldier.cpp
  2. #include<iostream>
  3. #include"Soldier.h"
  4. using namespace std;
  5. Soldier::Soldier()
  6. {
  7. cout << "Soldier() 构造" << endl;
  8. }
  9. void Soldier::play()
  10. {
  11. cout << "Soldier---play()" << endl;
  12. }
  13. void Soldier::work()
  14. {
  15. cout << "Soldier---work()" << endl;
  16. }

 


 
 
  1. //main.cpp
  2. #include<iostream>
  3. #include"Soldier.h"
  4. using namespace std;
  5. /*************************
  6. 继承关系中的 隐藏
  7. 要求:
  8. 1 定义Person类
  9. 数据成员: m_strName
  10. 成员函数:构造函数, play()
  11. 2 定义Soldier类:
  12. 数据成员: 无
  13. 成员函数:构造函数, play() worker()
  14. **************************/
  15. int main()
  16. {
  17. Soldier soldier;
  18. soldier.play();
  19. cout << "-------------------" << endl;
  20. soldier.Person::play();
  21. soldier.work();
  22. cin.get();
  23. return 0;
  24. }

运行结果:

 

 

3.2 is-a

如下图所示, 工人和人是一种 is-a 关系, 士兵和人同样也是 is-a 关系。但工人和士兵不是 is-a 关系

 

 

 

下面从内存角度 来说明 is-a 的关系:

 

 

代码示例

示例1:

要求:继承关系中的 is-a
1 定义Person类
    数据成员: m_strName 
    成员函数:构造函数  析构函数 play()

2 定义Soldier类:
    数据成员: m_iAge
    成员函数:构造函数  析构函数  worker()


 
 
  1. //Person.h
  2. #pragma once
  3. #include<iostream>
  4. #include<string>
  5. using namespace std;
  6. class Person
  7. {
  8. public:
  9. Person( string name= "pesrson_001"); //构造函数
  10. ~Person(); //析构函数
  11. void play();
  12. protected:
  13. string m_strName;
  14. };

 
 
  1. //Person.cpp
  2. #include<iostream>
  3. #include"Person.h"
  4. using namespace std;
  5. Person::Person( string name)
  6. {
  7. m_strName = name;
  8. cout << "Person()--构造" << endl;
  9. }
  10. Person::~Person()
  11. {
  12. cout << "~Person()--析构" << endl;
  13. }
  14. void Person::play()
  15. {
  16. cout << "Name: " << m_strName << endl;
  17. cout << "Person--play()" << endl;
  18. }

 


 
 
  1. //Soldier.h
  2. #pragma once
  3. #include<iostream>
  4. #include"Person.h"
  5. class Soldier: public Person
  6. {
  7. public:
  8. Soldier( string name = "soldier_001", int age = 20);
  9. ~Soldier();
  10. void work();
  11. protected:
  12. int m_iAge;
  13. };

 
 
  1. //Soldier.cpp
  2. #include<iostream>
  3. #include"Soldier.h"
  4. using namespace std;
  5. Soldier::Soldier( string name, int age)
  6. {
  7. m_strName = name;
  8. m_iAge = age;
  9. cout << "Soldier()--构造" << endl;
  10. }
  11. Soldier::~Soldier()
  12. {
  13. cout << "~Soldier--析构" << endl;
  14. }
  15. void Soldier::work()
  16. {
  17. cout << "Name= "<< m_strName << endl;
  18. cout << "Age= " << m_iAge << endl;
  19. cout << "Soldier--work()" << endl;
  20. }

 


 
 
  1. //main.cpp
  2. #include<iostream>
  3. #include"Soldier.h"
  4. using namespace std;
  5. /*************************
  6. 继承关系中的 is-a
  7. 要求:
  8. 1 定义Person类
  9. 数据成员: m_strName
  10. 成员函数:构造函数 析构函数 play()
  11. 2 定义Soldier类:
  12. 数据成员: m_iAge
  13. 成员函数:构造函数 析构函数 worker()
  14. **************************/
  15. int main()
  16. {
  17. Soldier soldier1;
  18. Person person1 ;
  19. soldier1.play();
  20. person1.play();
  21. cout << "" << endl;
  22. cout << "---------- 1 -----------" << endl;
  23. cout << "" << endl;
  24. Soldier soldier2;
  25. Person person2 = soldier2; //soldier2初始化person2
  26. person2.play(); //此时 person2中的m_strName被 soldier2初始化时更改了
  27. //因为 m_strName是继承下来的 所以可以通过子类对父类继承下来的成员进行赋值
  28. cout << "" << endl;
  29. cout << "---------- 2 -----------" << endl;
  30. cout << "" << endl;
  31. //使用指针 对继承父类的成员进行赋值
  32. Soldier soldier3;
  33. Person *p1 = &soldier3;
  34. p1->play(); //与上面的效果一样,对父类的成员进行赋值了
  35. cout << "" << endl;
  36. cout << "---------- 3 -----------" << endl;
  37. cout << "" << endl;
  38. //看 释放内存 执行 哪一个析构函数
  39. Person *p2 = new Soldier; //父类指针 指向子类
  40. p2->play();
  41. delete p2;
  42. p2 = NULL; //只执行 父类的析构函数 可能造成内存泄漏 这时 需要用 虚函数(在父类和子类前加入 virtual)
  43. cin.get();
  44. return 0;
  45. }

运行结果:

 

总结:

由结果可以知道:

(1) 子类可以赋值给父类,只能赋值继承下来的成员;

(2) 父类指针指向子类时,只能指向子类继承下来的成员;

(2) 在父类指针 指向 子类时,释放内存 只执行了父类的析构函数,这可能会造成内存泄漏 这时 需要用 虚函数(在父类和

     子类前加入 virtual) 即可。

 

 

示例2:

继承关系中的 is-a 函数传递 
要求:
1 定义Person类
    数据成员: m_strName 
    成员函数:构造函数  析构函数 play()

2 定义Soldier类:
    数据成员: m_iAge
    成员函数:构造函数  析构函数  worker()

3 定义函数test1(Person p) test2(person &p) test3(Person *p)

这里只有main.cpp不同 其他函数文件均与示例1相同。


 
 
  1. //main.cpp
  2. #include<iostream>
  3. #include"Soldier.h"
  4. using namespace std;
  5. /*************************
  6. 继承关系中的 is-a
  7. 要求:
  8. 1 定义Person类
  9. 数据成员: m_strName
  10. 成员函数:构造函数 析构函数 play()
  11. 2 定义Soldier类:
  12. 数据成员: m_iAge
  13. 成员函数:构造函数 析构函数 worker()
  14. 3 定义函数test1(Person p) test2(person &p) test3(Person *p)
  15. **************************/
  16. void test1(Person p)
  17. {
  18. p.play();
  19. }
  20. void test2(Person &p)
  21. {
  22. p.play();
  23. }
  24. void test3(Person *p)
  25. {
  26. p->play();
  27. }
  28. int main()
  29. {
  30. Person p;
  31. Soldier s;
  32. cout << "" << endl;
  33. cout << "---------- 1 -----------" << endl;
  34. test1(p); //接受值时 临时实例化 通过临时对象 调用函数play 结束后 对象自动销毁(调用析构函数)
  35. cout << "------------------------" << endl;
  36. test1(s);
  37. cout << "" << endl;
  38. cout << "---------- 2 -----------" << endl;
  39. cout << "" << endl;
  40. test2(p); //没有产生新的临时对象
  41. cout << "------------------------" << endl;
  42. test2(s);
  43. cout << "" << endl;
  44. cout << "---------- 3 -----------" << endl;
  45. cout << "" << endl;
  46. test3(&p); //没有产生新的临时对象
  47. cout << "------------------------" << endl;
  48. test3(&s);
  49. cin.get();
  50. return 0;
  51. }

运行结果:

注:由实验结果可知,test1(). test2()和test3() 三个函数调用时,调用test1()函数,会产生新的临时对象,而test2() 和test3()函数不会,所以test2() 和test3()函数的效率更高。

 

4 多继承与多重继承

 

5 虚继承

由于视频教程知识点较多,所以分开记录,便于查找和复习。详见:C++ 继承(2): 多重继承, 多继承, 虚继承(virtual)

 

 

参考资料

[1] C++远征之继承篇  (注:图片均来自视频中PPT)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值