C++学习笔记(三) 继承问题的讨论

基类和派生类

继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。
当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
C++中有分三种继承方式,如下表所示。
在这里插入图片描述
这里不去过多地讨论什么是继承,这里对继承时产生的一些事进行讨论。

继承时,构造函数做了什么

话不多说,上代码

#include <iostream>
using namespace std;

class Father
{
    
    
   public:
      Father()
      {
    
    
        cout<<"this is Father()"<<endl;
      }
};

class Son : public Father
{
    
    
   public:
      Son()
       {
    
    
          cout<<"this is son()"<<endl;  
       }
};

int main()
{
    
    
   Son p;
   return 0;
}

运行结果

this is Father()
this is son()

在上面的代码中,我在Father这个基类,和Son这个派生类里面分别添加了构造函数。在主程序里面定义了一个派生类的实例化对象。然而从运行结果里发现,分别进行了Father的构造函数和Son的构造函数,从而得出结论:在定义基类的实例化对象时,不仅会执行自身的构造函数,还会执行派生类的构造函数。
既然执行了构造函数,那么他们执行的先后顺序是什么样的呢,从上述的代码中,可以看出基类的构造函数是执行在派生类之前的。那么遇到多个继承关系,他们的继承关系又是什么样的呢?
为了内容不那么长,这里省略部分代码

class GrandFather{
    
    ...};
class Mother : public GrandFather{
    
    ...};
class Mr_Wang {
    
    };
class Son : public GrandFather, virtual public Mr_Wang{
    
    ...};

int main()
{
    
    
   Son s;
   return 0;
}

由上述程序可见,继承关系如图所示
在这里插入图片描述
这里Mr_Wang和Son的关系是虚拟继承(虚拟继承不在本次讨论范围内),所以这里用红色表示。
程序的运行结果如下

Mr_Wang
GrandFather
mother
Son

从运行结果中,不难看出
虚拟继承构造函数的优先级最高,其次,基类的优先级会高于派生类

使用基类指针指向派生类,会发生什么

下面分两点来讨论

使用基类指针指向派生类,构造函数的顺序是什么样

话不多说,依旧上代码

#include <iostream>
using namespace std;

class Father
{
    
    
   public:
      Father()
      {
    
    
        cout<<"this is Father()"<<endl;
      }
};

class Son : public Father
{
    
    
   public:
      Son()
       {
    
    
          cout<<"this is son()"<<endl;  
       }
};

int main()
{
    
    
   Father *p = new Son;
   delete p;
   p = NULL;
   return 0;
}

执行结果

this is Father()
this is son()

从运行结果中,不难看出,基类指针指向派生类,构造函数还是会先构造基类,再构造派生类。

使用基类指针指向派生类,对于属性的访问是什么样的

这里先说结论:使用基类指针指向派生类,该指针只能引用基类成员。
下面修改代码

#include <iostream>
using namespace std;

class Father
{
    
    
   public:
      Father()
      {
    
    
        cout<<"this is Father()"<<endl;
      }
      void test_func_one()
      {
    
    
              cout<<"this is test_func_one";
      }
};

class Son : public Father
{
    
    
   public:
      Son()
       {
    
    
          cout<<"this is son()"<<endl;
       }
      void test_func_two()
      {
    
    
              cout<<"this is test_func_two"<<endl;
      }
};

int main()
{
    
    
   Father *p = new Son;
   p->test_func_two();
   delete p;
   p = NULL;
   return 0;
}

这里我定义了一个基类对象的指针,指向派生类对象。意图去访问派生类里面的函数。看一下编译结果。

error: ‘class Father’ has no member named ‘test_func_two’; did you mean ‘test_func_one’?
    p->test_func_two();
       ^~~~~~~~~~~~~

显然,报错了。不能访问。验证了刚刚的结论。

那么我如果非要访问派生类对象怎么办呢?

继续修改代码

#include <iostream>
using namespace std;

class Father
{
    
    
   public:
      Father()
      {
    
    
        cout<<"this is Father()"<<endl;
      }
      virtual void test_func_one()
      {
    
    
              cout<<"this is test_func_one";
      }
};

class Son : public Father
{
    
    
   public:
      Son()
       {
    
    
          cout<<"this is son()"<<endl;
       }
      void test_func_one()
      {
    
    
              cout<<"this is test_func_two"<<endl;
      }
};

int main()
{
    
    
   Father *p = new Son;
   p->test_func_one();
   delete p;
   p = NULL;
   return 0;
}

编译通过,看一下运行结果

this is Father()
this is son()
this is test_func_two

通过以上现象,不难发现。
基类对象的指针如果指向派生类对象,不能直接访问派生类对象的属性。如果要访问,可以在基类里面定义虚函数,在派生类里对这个虚函数进行重新定义。就可以访问派生类对象。

这种花里胡哨的用法,有没有什么实际用途

我们可以通过基类来定义一个模板,派生类用来定义一些模板往上的东西。这么说可能有点抽象,举个具体的例子。
比如我们要做摄像头,不同型号的摄像头可能具体实现的操作不太一样,但是流程应该都差不多。所以可以在基类里面声明一些Init,Start这类操作的虚函数作为模板。然后我们可以在派生类里针对具体的摄像头型号来重新定义这些虚函数。

猜你喜欢

转载自blog.csdn.net/Stone831143/article/details/110596269