C++基类、派生类、虚函数的几个知识点

1.尽管派生类中含有基类继承来的成员,但派生类初始化这部分变量需要调用基类的构造函数。

1 class A
 2 {
 3 private:
 4     int x;
 5     virtual void f(){cout<<"A f"<<endl;}
 6 public:
 7     A(/* args */){x=1;cout<<"A构造"<<endl;}
 8     ~A(){}
 9     friend void p(const A& a){cout<<a.x;}
10 };
11 class B:A{
12     public:
13     void f() override {cout<<"B f"<<endl;}
14     operator A()const {}
15 };
16 int main()
17 {
18     B x;
19     system("pause");
20     return 0;
21 }

C++基类、派生类、虚函数的几个知识点

2.如果基类定义了一个静态成员,则整个继承体系中只存在该成员的唯一定义。并且不论基类派生出多少派生类,该静态成员只存在唯一实例。

并且属性也一致。基类中某静态成员是public,派生类中也是public。如果基类中是private,那么派生类也无法调用该静态成员。

1 /* 基类的静态成员 */
 2 
 3 #include<iostream>
 4 
 5 using namespace std;
 6 
 7 class base
 8 {
 9 public:
10     static int num;
11 
12     base()
13     {
14         num+=1;
15     }
16 
17     ~base()
18     {
19         num-=1;
20     }
21 };
22 
23 // 基类定义的静态成员,被所有派生类共享 
24 // 起到计数器的作用,自身还有所有派生类对象的个数
25 // 遵循私有 公有 保护的继承机制 访问不同
26 // 可以通过父类  子类对象访问 还可以通过父类  子类类名访问
27 int base::num = 0;// 类的静态成员
28 
29 class basenew : public base
30 {
31     
32 };
33 
34 class basenewx : public basenew
35 {
36     
37 };
38 
39 
40 void main()
41 {
42     basenew *p = new basenew[100];
43 
44     base *p1 = new base[40]
45 
46     basenewx *p2 = new basenewx[50];
47     
48     cout << p->num << endl;// 190
49     cout << p1->num << endl;// 190
50     cout << p2->num << endl;// 190
51 
52 
53     cin.get();
54 }

3.如果不想让某个类被继承,在类名后加final关键字。

C++基类、派生类、虚函数的几个知识点

final除了可以修饰类外,还可以修饰成员函数。还可以指明某个基类的虚函数不能被其派生类版本覆盖,如下:

首先要明确覆盖(override)与重载(overload)的定义,区别出什么是覆盖和重载:

覆盖就是派生类中虚成员函数覆盖基类中同名且参数相同的成员函数。

1 class A
 2 {
 3 public:
 4     A(/* args */){}
 5     virtual void f()final {}
 6     virtual ~A(){}
 7 };
 8 class B:public A{
 9     public:
10     void f(int x){}//重载(overload)
11     void f(){}  //覆盖(override,非法,因为A中的f声明了final)
12 };

4.派生类对象是基类对象,派生类中包含有基类的成员。基类对象不是派生类对象,它不能包含派生类型的成员。

派生类可以向基类转化(仅限指针和引用),基类不能向派生类转化(毕竟派生类有多余的数据,基类没法自己生成)

如果用派生类对象为一个基类对象初始化或者赋值,只有其中的基类部分会被拷贝/移动/赋值,它的派生类部分会被忽略。

1 class A
 2 {
 3 private:
 4     int x;
 5 public:
 6     A(/* args */){x=1;cout<<"A构造"<<endl;}
 7     virtual ~A(){}
 8 };
 9 class B:public A{
10     
11 };
12 int main()
13 {
14     A x;
15     B y;
16     A* p=&y;//正确,将基类指针指向派生类变量,派生类指针隐式转换为基类指针。
17     B* q=&x;//错误
18     system("pause");
19     return 0;
20 }

5.动态绑定(即dynamic bninding)只有当我们通过指针或者引用调用虚函数时才会发生。

由于继承导致对象的指针和引用具有两种不同的类型:静态类型和动态类型。

静态类型:指针或者是引用声明时的类型。

动态类型:由实际指向的类型确定。

其中静态类型编译时就已经确定,动态类型只有到运行的时候才能知道。所以如果用普通类类型调用虚函数,编译时就会把要调用的虚函数版本确定下来。

1 class A
 2 {
 3 public:
 4     A(/* args */){}
 5     virtual void f(){cout<<"基类的f"<<endl;}
 6     virtual ~A(){}
 7 };
 8 class B:public A{
 9     public:
10     void f(){cout<<"派生类的f"<<endl;}
11 };
12 void F(A& p){
13     p.f();
14 }
15 int main()
16 {
17     A x;
18     B y;
19     F(x);
20     F(y);
21     system("pause");
22     return 0;
23 }

C++基类、派生类、虚函数的几个知识点

可以看到同一个函数F,因为调用不同类型实参,最终在内部调用了不同版本的f函数,这就是动态绑定。

如果F的形参改成普通类型,那么两次都会调用基类的f函数,其中F(y)会把y隐式类型转换为一个A类型的临时变量传给形参。

C++基类、派生类、虚函数的几个知识点

6.如果一个派生类虚函数需要调用其基类版本,但没有使用“基类::”修饰,则运行时该调用会被解析为对其自身的调用,这将导致无限递归。

7.派生类中的虚函数可以在形参后加override,显式告知编译器该虚函数覆盖了其基类版本。

原文链接: https://www.cnblogs.com/FdWzy/p/12374204.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/193654

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午6:27
下一篇 2023年2月12日 下午6:27

相关推荐