第六层:继承(下)

简介: 第六层:继承(下)

静态成员


静态成员的访问有两种:


1.通过对象访问

2.通过类名访问

对于这两个方法,第一种与非静态成员是一样的,可以验证一下第二种——通过类名访问:


#include<iostream>
using namespace std;
class A
{
public:
  static void a()
  {
  cout << "父类中调用" << endl;
  }
};
class A1:public A
{
public:
  static void a()
  {
  cout << "子类中调用" << endl;
  }
};
void test1()
{
  A1 a1;
  A1::a();
  A1::A::a();
}
int main()
{
  test1();
  return 0;
}


0a2653c851af460fa595bd959398a8f1.png

静态函数发生函数重载时也与非静态函数发生函数重载一样。


多继承


在C++种,允许一个类继承多个类,这个时候就是多继承


语法


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


注意


在多继承中,多个父类中可能有同名成员出现,这个时候需要加作用域来区分


多继承中的对象模型


那当子类继承多个父类的时候,每个父类内的非静态成员都会被继承过去吗?通过代码验证一下:


#include<iostream>
using namespace std;
class A
{
public:
  int a;
  int b;
};
class A1
{
public:
  int _a;
  int _b;
};
class AA :public A, public A1
{
public:
  int _a1;
  int _b1;
};
void test1()
{
  AA aa;
  cout << sizeof(aa) << endl;
}
int main()
{
  test1();
  return 0;
}


0a2653c851af460fa595bd959398a8f1.png

和普通继承一样,每个父类的非静态成员都会被继承到子类当中。


多继承父类成员名相同


假设现在两个父类,有两个成员名时一样的,那子类能直接访问这个成员吗?

#include<iostream>
using namespace std;
class A
{
public:
  int _a;
  int b;
};
class A1
{
public:
  int _a;
  int _b;
};
class AA :public A, public A1
{
public:
  int _a1;
  int _b1;
};
void test1()
{
  AA aa;
  aa._a;
}
int main()
{
  test1();
  return 0;
}


0eacb84100b54626af849e6b562bf92a.png

0eacb84100b54626af849e6b562bf92a.png

编译器是直接报错的,是因为编译器不知道这个成员是来自与哪个父类,这个情况在C++中有个专业名词,叫做二义性,这个时候要怎么样才能访问到呢?就需要进行加作用域,假设现在访问A内的_a:


#include<iostream>
using namespace std;
class A
{
public:
  int _a;
  int b;
};
class A1
{
public:
  int _a;
  int _b;
};
class AA :public A, public A1
{
public:
  int _a1;
  int _b1;
};
void test1()
{
  AA aa;
  aa.A::_a = 10;
  cout<<aa.A::_a<<endl;
}
int main()
{
  test1();
  return 0;
}

2d65d23f6d4748949b924e4057485923.png


菱形继承


概念


两个子类继承了同一个父类,又有一个类,同时继承了两个子类


0a2653c851af460fa595bd959398a8f1.png


菱形继承出现的问题


马和驴都继承了动物的数据,在骡子使用数据的时候,就会产生二义性,骡子继承动物的数据只需要一份,那应该怎么办?

虚继承

对于上面所说的二义性的问题,因为同样的数据只需要一份,这个时候就可以在继承的时候加上关键字:


vritual

将继承变成虚继承,这个时候最大的父类就为虚基类,可以用代码试验:


#include<iostream>
using namespace std;
class A
{
public:
  int a;
  int b;
};
class AA:virtual public A
{
};
class AA1:virtual public A
{
};
class AAA :public AA, public AA1
{
};
void test1()
{
  AAA a;
  a.AA::a = 10;
  a.AA1::a = 20;
  cout << a.AA::a << endl;
  cout << a.AA1::a << endl;
}
int main()
{
  test1();
  return 0;
}

0eacb84100b54626af849e6b562bf92a.png

发现,两个数据修改输出的结果是一样的,这个时候就是虚继承起到的作用,让将内部数据共享了一个,这个时候最小的子类也可以直接访问到这个属性:


#include<iostream>
using namespace std;
class A
{
public:
  int a;
  int b;
};
class AA:virtual public A
{
};
class AA1:virtual public A
{
};
class AAA :public AA, public AA1
{
};
void test1()
{
  AAA a;
  a.AA::a = 10;
  a.AA1::a = 20;
  cout << a.a << endl;
}
int main()
{
  test1();
  return 0;
}

2d65d23f6d4748949b924e4057485923.png

这是为什么呢?那这个时候从父类继承下来些什么?这个时候从父类继承下来一个指针:


vbptr

这个指针叫做虚基类指针,它会指向一个叫做:


vbtable

虚基类表,虚基类表中有一个偏移量,这个偏移量会指向一个数据,每个子类指向的数据都是同一份,这个时候数据就只会有一个。

2e9b90b2ca334476abebe75bafe6eeaa.png


步入第七层


石碑倒下,大片尘土飞扬,尘土散去,只留下一串脚印,通往第七层…


本章知识点(图片形式)


4cebaac233b3433da32a72337a77fc60.png


😘预知后事如何,关注新专栏,和我一起征服C++这座巨塔

🚀专栏:C++爬塔日记

🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉


相关文章
|
7月前
|
Java 编译器 程序员
关于继承是怎么样的?那当然是很好理解之
关于继承是怎么样的?那当然是很好理解之
34 1
|
7月前
|
安全 编译器 程序员
【C++】—— 继承
【C++】—— 继承
|
9天前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
23 1
【C++】继承
|
7天前
继承
1、使用关键字 extends进行继承,只允许多层继承,不允许多重继承,即一个子类只能继承一个父类,一个父类还可以有一个父类。 2、子类不能直接访问父类中的私有成员,可以调用父类的非私有方法 3、子类的实例化 1)先调用父类构造 2)再调用子类构造
13 0
|
6月前
|
编译器 C++
C++中的继承
C++中的继承
48 1
|
7月前
|
编译器 C++
【继承】初步了解C++继承
【继承】初步了解C++继承
|
7月前
|
存储 Java 编译器
C++:继承
C++:继承
43 1
|
存储 编译器 程序员
c++【继承】
C++ 继承,包括继承的概念和用法,菱形继承的产生,组合的介绍等丰富知识点,详细讲解,干货满满!
43 4
c++【继承】
|
7月前
|
存储 设计模式 Java
C++【继承】
C++【继承】
74 0
|
7月前
|
安全 编译器 程序员
【C++】:继承
【C++】:继承
56 0