23.C++- 继承的多种方式、显示调用父类构造函数、父子之间的同名函数、virtual虚函数  上章链接: 22.C++- 继承与组合,protected访问级别

继承方式

继承方式位于定义子类的”:”后面,比如:

class Line : public Object             //继承方式是public
{

};

继承方式默认为private

在C++中,继承方式共有3种:

public继承

-指父类的成员(变量和函数)访问级别,在子类中保持不变

private继承

-指父类的成员,在子类中变为private私有成员.

-也就是说子类无法访问父类的所有成员

protected继承

-指父类的public成员 ,在子类中变为protected保护成员,其它成员级别保持不变

如下图所示:

注意: protected继承只针对子类有效

比如当父类是protected继承时,则子类的子类就无法访问父类的所有成员

一般而言,C++项目只用到public继承

显示调用父类构造函数

  • 当我们创建子类对象时,编译器会默认调用父类无参构造函数
  • 若有子类对象,也会默认调用子类对象的无参构造函数。

比如以下代码:

class  StrA
{
public:
          StrA()
          {
             cout<<"StrA()"<<endl;
          }
          StrA(string s)
          {
             cout<<"StrA(string s):"<<s<<endl;
          } 
};

class  StrB : public StrA
{
public:
          StrB(string s)
          {
             cout<<"StrB(int i):"<<s<<endl;
          }
};

int main()
{
       StrB b("123");
       return 0;
}

 编译运行:

StrA()                    //父类无参构造函数
StrB(int i):123

也可以通过子类构造函数的初始化列表来显示调用

接下来,修改上面子类的StrB(string s)函数,通过初始化列表调用StrA(string s)父类构造函数

改为:

StrB(string s): StrA(s)
{
  cout<<"StrB(int i):"<<s<<endl;
}

运行打印:

StrA(string s):123
StrB(int i):123

父子间的同名成员和同名函数

  • 子类可以定义父类中的同名成员和同名函数
  • 子类中的成员变量和函数将会隐藏父类的同名成员变量和函数
  • 父类中的同名成员变量和函数依然存在子类中
  • 通过作用域分辨符(::)才可以访问父类中的同名成员变量和函数

比如:

class Parent{

public:
       int mval;
       Parent()
       {
              mval=1000;
       }

       void add(int i)
       {
              mval+=i;
       } 
};

class Child : public Parent
{
public:
       int mval; 
       Child()
       {
              mval=100;
       }

       void add(int i,int j)
       {
              mval+=i+j;
       }
};

在main()函数执行:

       Child c;

       //c. add(10);        //该行会报错,由于子类有add函数,所以编译器会默认在子类里寻找add(int i);

       c.Parent::add(10);   //该行正确,执行父类的成员函数

       c.add(2,3);

       cout<<"Child.mval="<<c.mval<<endl;

       cout<<"Parent.mval="<<c.Parent::mval<<endl;

打印:

Child.mval=105
Parent.mval=1010

从打印结果看到,父类和子类之间的作用域是不同的, 所以执行父类的同名成员变量和函数需要作用域分辨符(::)才行

父子间的兼容

以上示例的Parent父类Child子类为例

  • 子类对象可以直接赋值给父类对象使用,比如: Parent p; Child c;   p=c;
  • 子类对象可以初始化父类对象,比如: Parent p1(c);
  • 父类引用可以直接引用子类对象,比如: Parent& p2 =c;    //p2是c对象的别名
  • 父类指针可以直接指向子类对象,比如: Parent* p3=&c;

其实是编译器是将子类对象退化为了父类对象, 从而能通过子类来赋值初始化父类

所以上述的父类对象(包括指针/引用)也只能访问父类中定义的成员.

如果父类对象想访问子类的成员,只能通过强制转换,将父类对象转为子类类型

示例1,通过C方式转换:

Child c;
Parent* p3=&c;
Child *c2 = (Child*)p3;         

示例2,通过static_cast转换:

Child c;
Parent* p3=&c;
Child *c2 = (static_cast*)<Child*>(p3);

虚函数

实现多态性,通过指向子类的父类指针或引用,可以访问子类中同名覆盖成员函数

首先参考下面,没有虚函数的示例:

class Parent
{
    int i; 
public:  
         void example()
        {
            cout<<"class Parent"<<endl;
        }

}; 
 
class Child : public Parent  
{
    int j; 
public:
        void example()
        {
            cout<<"class Child"<<endl;
        }     
};


void print(Parent* p)
{
     p->example();
}
int main()
{
    Parent t; 
    Child c;
    
    print(&t);
    print(&c);     
    
    cout<<"SIZEOF Parent:"<<sizeof(t)<<endl;
    cout<<"SIZEOF Child:"<<sizeof(c)<<endl; 
}

运行打印:

class Parent
class Parent
SIZEOF Parent:4
SIZEOF Child:8

从结果看出,即使example函数指针p指向了Child c,也只能调用父类的example(),无法实现多态性.

所以C++引入了虚函数概念,根据指针指向的对象类型,来执行不同类的同名覆盖成员函数,实现不同的形态

定义: 在父类成员函数的返回值前面,通过virtual关键字声明,这样便能访问子类中的同名成员函数了

接下来将上个示例的父类成员函数example()改写为虚函数:

virtual void print()        //将父类的成员函数定为虚函数
{
cout<<"class Parent"<<endl;
}        

运行打印:

class Parent
class Child
SIZEOF Parent:8
SIZEOF Child:12

可以发现,父类和子类的长度都增加了4字节,这4个字节就是用来指向“虚函数表”的指针,编译器便会更据这个指针来执行不同类的虚函数,实现多态性.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android群英传

Swift vs. Kotlin 漫谈系列之类与继承

2064
来自专栏Android干货园

Kotlin中级(8)- - - Kotlin类之接口、枚举.md

772
来自专栏程序猿DD

第二章 正则表达式位置匹配攻略

第二章 正则表达式位置匹配攻略 正则表达式是匹配模式,要么匹配字符,要么匹配位置。请记住这句话。 然而大部分人学习正则时,对于匹配位置的重视程度没有那么高。 本...

21810
来自专栏游戏开发那些事

【Cocos2d-x游戏开发】细数Cocos2d-x开发中那些常用的C++11知识

  自从Cocos2d-x3.0开始,Cocos2dx就正式的使用了C++11标准.C++11简洁方便的特性使程序的可拓展性和可维护性大大提高,也提高了代码的书...

1083
来自专栏用户3030674的专栏

java上转型和下转型(对象的多态性)

/*上转型和下转型(对象的多态性) *上转型:是子类对象由父类引用,格式:parent p=new son *也就是说,想要上转型的前提必须是有继承关系的两...

3821
来自专栏游戏杂谈

AS3.0中类初始化的顺序

父类--静态变量 子类--静态变量 父类--变量 父类--构造器 子类--变量 子类--构造器

1164
来自专栏C语言C++游戏编程

有人@我,你有一份C语言基础大全手册要领取,快来拿!

前两天,有网友问了我一个关于C语言的问题,本着认真装逼的态度,我把大学时学过的C语言课本翻了一遍,终于找到了答案。整理后,现分享给大家!

1432
来自专栏CDA数据分析师

Python 面试中8个必考问题

? 翻译 everfighting 原文链接:https://www.toptal.com/python/interview-questions Q1、下...

3559
来自专栏C++

python笔记:#005#算数运算符

1632
来自专栏C语言及其他语言

【蓝桥杯系列】第一节 C的基本用法

置顶编程范收获更多热门编程快讯 大家好,最近很多小伙伴向我反应小编!我参加了蓝桥杯但是我连那是什么都不知道,我该怎么训练?是不是在网站刷题就可以啊? 在这里我要...

4057

扫码关注云+社区

领取腾讯云代金券