黑马程序员C++类和对象【6】—— 继承(一文万字带你搞懂C++继承 —— 你还不知道怎么学C++继承吗?不知道虚继承底层原理吗?)(2)

简介: 黑马程序员C++类和对象【6】—— 继承(一文万字带你搞懂C++继承 —— 你还不知道怎么学C++继承吗?不知道虚继承底层原理吗?)(2)

继承同名成员处理方式

问题:

当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可(就近原则,正常访问的是子类的)
  • 访问父类同名成员 需要加作用域
class Base {
public:
  Base()
  {
    m_A = 100;
  }
  void func()
  {
    cout << "Base - func()调用" << endl;
  }
  void func(int a)
  {
    cout << "Base - func(int a)调用" << endl;
  }
public:
  int m_A;
};
class Son : public Base {
public:
  Son()
  {
    m_A = 200;
  }
  //如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
  void func()
  {
    cout << "Son - func()调用" << endl;
  }
public:
  int m_A;
};
void test01()
{
  Son s;
  cout << "Son下的m_A = " << s.m_A << endl;
  cout << "Base下的m_A = " << s.Base::m_A << endl;
  s.func();
  s.Base::func();
  //当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数(只要是func的全部隐藏,不能直接访问),想要访问只能加作用域。
    s.func(100);    //报错
  s.Base::func(10);
}
int main() {
  test01();
  system("pause");
  return EXIT_SUCCESS;
}

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

继承同名静态成员处理方式

问题:

       继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域

示例:

class Base {
public:
  static void func()
  {
    cout << "Base - static void func()" << endl;
  }
  static void func(int a)
  {
    cout << "Base - static void func(int a)" << endl;
  }
  //静态成员属性(编译阶段分配到内存,所有对象共享同一份数据)
  static int m_A;
};
//类内声明,类外初始化
int Base::m_A = 100;
class Son : public Base {
public:
  static void func()
  {
    cout << "Son - static void func()" << endl;
  }
  static int m_A;
};
int Son::m_A = 200;
//同名成员属性
void test01()
{
  //通过对象访问
  cout << "通过对象访问: " << endl;
  Son s;
  cout << "Son  下 m_A = " << s.m_A << endl;
  cout << "Base 下 m_A = " << s.Base::m_A << endl;
  //通过类名访问(静态成员变量专属福利)
  cout << "通过类名访问: " << endl;
  cout << "Son  下 m_A = " << Son::m_A << endl;
    //第一个::代表通过类目方式访问,第二个::代表访问父类作用域下
  cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
//同名成员函数
void test02()
{
  //通过对象访问
  cout << "通过对象访问: " << endl;
  Son s;
  s.func();
  s.Base::func();
  cout << "通过类名访问: " << endl;
  Son::func();
  Son::Base::func();
  //出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
  Son::Base::func(100);
}
int main() {
  //test01();
  test02();
  system("pause");
  return 0;
}

总结:

同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)

多继承语法

C++允许一个类继承多个类

语法:

   class 子类 :继承方式 父类1 , 继承方式 父类2...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

       C++实际开发中不建议用多继承

示例:

class Base1 {
public:
  Base1()
  {
    m_A = 100;
  }
public:
  int m_A;
};
class Base2 {
public:
  Base2()
  {
    m_A = 200;  //开始是m_B 不会出问题,但是改为mA就会出现不明确
  }
public:
  int m_A;
};
//子类需要继承Base1和Base2
//语法:class 子类:继承方式 父类1 ,继承方式 父类2 …
class Son : public Base2, public Base1 
{
public:
  Son()
  {
    m_C = 300;
    m_D = 400;
  }
public:
  int m_C;
  int m_D;
};
//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
  Son s;
    //16    也可以使用开发者工具验证(前面已经说了)
  cout << "sizeof Son = " << sizeof(s) << endl;
    //当父类中出现同名成员,需要加作用域区分
  cout << s.Base1::m_A << endl;
  cout << s.Base2::m_A << endl;
}
int main() {
  test01();
  system("pause");
  return 0;
}

sizeof的结果:

总结:

       多继承中如果父类中出现了同名情况,子类使用时候要加作用域

菱形继承(C++特色)

菱形继承概念:

       两个派生类继承同一个基类

       又有某个类同时继承者两个派生类

       这种继承被称为菱形继承,或者钻石继承

菱形继承问题:

  1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
  2. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
//动物类
class Animal
{
public:
  int m_Age;
};
//利用虚继承,解决菱形继承问题。
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};        //羊类
class Tuo   : virtual public Animal {};        //驼类
class SheepTuo : public Sheep, public Tuo {};  //羊驼类
void test01()
{
  SheepTuo st;
  st.Sheep::m_Age = 10;
  st.Tuo::m_Age = 20;
    //当出现菱形继承的时候,两个父类拥有系统数据,需要加以作用域区分
  cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
  cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
    //这份数据我们知道,只要有一份就行了,菱形继承导致数据有两份,资源浪费。
  cout << "st.m_Age = " << st.m_Age << endl;
}
int main() {
  test01();
  system("pause");
  return 0;
}

菱形继承的问题二:

加virtual虚继承之前:

利用虚继承,解决菱形继承的问题。

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题

虚继承解决菱形继承问题的底层原理图解:

虚继承解决菱形继承问题的底层剖析                      —— By CSDN码雨

结语:

       博客制作不易,画图有点费眼,要是觉得博客对您有帮助,请一键三连嗷!

                                                                                                     —— By CSDN码雨


相关文章
|
14天前
|
C++
C++(二十)继承
本文介绍了C++中的继承特性,包括公有、保护和私有继承,并解释了虚继承的作用。通过示例展示了派生类如何从基类继承属性和方法,并保持自身的独特性。此外,还详细说明了派生类构造函数的语法格式及构造顺序,提供了具体的代码示例帮助理解。
|
14天前
|
C++
C++(十一)对象数组
本文介绍了C++中对象数组的使用方法及其注意事项。通过示例展示了如何定义和初始化对象数组,并解释了栈对象数组与堆对象数组在初始化时的区别。重点强调了构造器设计时应考虑无参构造器的重要性,以及在需要进一步初始化的情况下采用二段式初始化策略的应用场景。
|
14天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
14天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
14天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
14天前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
1月前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
31 0
|
1月前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)
|
27天前
|
存储 算法 编译器
c++--类(上)
c++--类(上)
|
1月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决