• 类继承关系梳理


    1. class base {
    2. public:
    3. int hello() {
    4. std::cout << "base::hello" << std::endl;
    5. return 0;
    6. }
    7. int hello(std::string str) {
    8. std::cout << "base::hello(std::string str) " << str << std::endl;
    9. return 0;
    10. }
    11. int base_func() {
    12. std::cout << "base::base_func" << std::endl;
    13. return 0;
    14. }
    15. int virtual virtual_func() {
    16. std::cout << "base::virtual_func" << std::endl;
    17. return 0;
    18. }
    19. int virtual virtual_func(int a) {
    20. std::cout << "base::virtual_func(int a)" << std::endl;
    21. return 0;
    22. }
    23. int virtual virtual_base_func() {
    24. std::cout << "base::virtual_base_func" << std::endl;
    25. return 0;
    26. }
    27. int var = 0;
    28. };
    29. class derived : public base {
    30. public:
    31. //using base::hello;
    32. int hello() {
    33. std::cout << "derived::hello" << std::endl;
    34. return 0;
    35. }
    36. int derived_func() {
    37. std::cout << "derived::derived_func" << std::endl;
    38. return 0;
    39. }
    40. int virtual virtual_func() {
    41. std::cout << "derived::virtual_func" << std::endl;
    42. return 0;
    43. }
    44. int var = 1;
    45. };
    46. int32_t main (int32_t argc, char **argv) {
    47. //派生类测试
    48. derived der;
    49. der.base::hello();//基类
    50. //der.hello("word");//no matching function for call to 'derived::hello(const char [5])'因为hello被重定义后隐藏了
    51. der.base::hello("word");//基类 加上作用域后
    52. //der.hello("word");//基类 或者使用using后也可以执行
    53. der.base_func();//基类,可以直接调用因为没有重定义
    54. der.hello();//派生类
    55. der.virtual_func();//派生类
    56. //der.virtual_func(5);//no matching function for call to 'derived::virtual_func(int)'因为virtual_func被重定义后隐藏了?
    57. der.virtual_base_func();//基类
    58. std::cout << "base_ptr->var: " << der.var << std::endl;//派生类
    59. std::cout << std::endl;
    60. //基类指针指向派生类对象
    61. base * base_ptr = &der;
    62. //base_ptr->derived_func();//'class base' has no member named 'derived_func'
    63. //base_ptr->derived::derived_func();//'derived' is not a base of 'base'
    64. base_ptr->hello();//基类
    65. base_ptr->base::virtual_func();//基类
    66. base_ptr->virtual_func();//派生类
    67. base_ptr->virtual_func(5);//基类
    68. std::cout << "base_ptr->var: " << base_ptr->var << std::endl;//基类
    69. std::cout << std::endl;//基类
    70. return 0;
    71. }

    结果

    一、派生类对象调用测试说明

    Q:派生类能否调用基类的函数?

    A:可以并且如果没有重定义的话可以直接调用不用加作用域,如果重定义的话需要加using或者作用域 

    解析:派生类对象在调用函数时,会从派生类类中查找是否有该函数,发现没有后进一步查找基类

    Q:为什么重定义hello()函数后不能直接调用基类的hello(std::string str)

    A:   基类有很多重载函数,而派生类重定义了基类的函数,那么默认情况下,基类中的这些所有的重载函数都不可以使用了。参考hello函数

    如果我们希望只覆盖基类中的一部分函数,而其他函数在派生类中还没有被覆盖,一种使用方法就是使用using声明。这样的话就无须覆盖基类中的每一个重载版本了
    using声明只需要给出名称,而不需要给出参数列表,因此基类中的所有重载函数在派生类中都可以使用了
    注意:使用using声明时,当using在派生类的不同的访问模式(public、protected、private)下,那么基类的函数在派生类中就属于该访问模式

    另外:当一个派生类私有继承基类时,基类的public和protected数据成员在派生类中是private的形式,如果想让这些继承而来的数据成员作为public或者protected成员,可以用using来重新声明。using声明语句中名字的访问权限由该using声明语句之前的访问说明符决定。

    1. class Basic{
    2. public:
    3. int a;
    4. int b;
    5. };
    6. class Bulk : private Basic{
    7. public:
    8. using Basic::a;
    9. protected:
    10. using Basic::b;
    11. };

    二、基类指针指向派生类对象说明

    派生类对象可以赋值给基类的对象/基类的指针/基类的引用。有一种很形象的说法叫做切片或者切割。就是把派生类当中父类的那部分切来赋值过去。

    用基类指针绑定的子类对象,只能通过这个基类指针调用基类中的成员,因为作用域仅限于基类的子对象,子类新增的部分是看不见的

    Q:因为是切片所以如果基类和派生类同时拥有一个同名变量时候基类指针会使用基类变量/函数

    A:对

    Q:为什么虚函数会使用派生类

    A:因为虚函数是通过虚函数表实现原理可以参考下:C++中继承及virtual小结_c++ virtual 继承-CSDN博客s

    三、类作用域

    类其实也是一种作用域,每个类都会定义它自己的作用域,在这个作用域内我们再定义类的成员,这一点已在《类其实也是一种作用域》中讲到。当存在继承关系时,派生类的作用域嵌套在基类的作用域之内,如果一个名字在派生类的作用域内无法找到,编译器会继续到外层的基类作用域中查找该名字的定义。

    换句话说,作用域能够彼此包含,被包含(或者说被嵌套)的作用域称为内层作用域(inner scope),包含着别的作用域的作用域称为外层作用域(outer scope)。一旦在外层作用域中声明(或者定义)了某个名字,那么它所嵌套着的所有内层作用域中都能访问这个名字。同时,允许在内层作用域中重新定义外层作用域中已有的名字。

    B 继承自 A,C继承自 B,它们作用域的嵌套关系如下图所示:

    obj 是 C 类的对象,通过 obj 访问成员变量 n 时,在 C 类的作用域中就能够找到了 n 这个名字。虽然 A 类和 B 类都有名字 n,但编译器不会到它们的作用域中查找,所以是不可见的,也即派生类中的 n 遮蔽了基类中的 n。

    通过 obj 访问成员函数 func() 时,在 C 类的作用域中没有找到 func 这个名字,编译器继续到 B 类的作用域(外层作用域)中查找,仍然没有找到,再继续到 A 类的作用域中查找,结果就发现了 func 这个名字,于是查找结束,编译器决定调用 A 类作用域中的 func() 函数。

    这个过程叫做名字查找(name lookup),也就是在作用域链中寻找与所用名字最匹配的声明(或定义)的过程。
     

    参考文献

    C++:91---类继承(继承中类的作用域(附加:隐藏、重写(覆盖)))_mb6128aabee41d4的技术博客_51CTO博客

    C++中的继承/虚继承原理-CSDN博客

    【C++11】C++ 中using 的使用_51CTO博客_c++ using

    C++类继承时的作用域嵌套,破解C++继承的一切秘密_luoganttcc的博客-CSDN博客

  • 相关阅读:
    单点登录SSO的含义
    【23种设计模式】组合模式(八)
    【算法1-3】暴力枚举——组合的输出
    shell脚本中实现远程和其他用户的子shell执行
    4种实现JS深拷贝的方法
    大华sdk使用问题
    Vue——webpack项目(没有安装vue-cli脚手架时)解决请求项目启动问题、跨域问题和请求转发报404的问题
    React Context源码是怎么实现的呢
    react使用Table表格调整间距 居中样式
    hutool工具实践-缓存
  • 原文地址:https://blog.csdn.net/sinat_27652257/article/details/133385416