【C++】多态 ⑨ ( vptr 指针初始化问题 | 构造函数 中 调用 虚函数 - 没有多态效果 )

本文主要是介绍【C++】多态 ⑨ ( vptr 指针初始化问题 | 构造函数 中 调用 虚函数 - 没有多态效果 ),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、vptr 指针初始化问题
    • 1、vptr 指针与虚函数表
    • 2、vptr 指针初始化时机
    • 3、构造函数 中 调用 虚函数 - 没有多态效果
    • 4、代码示例


构造函数 的 作用就是 创建对象 , 构造函数 最后 一行代码 执行完成 , 才意味着 对象构建完成 , 对象构建完成后 , 才会将 vptr 指针 指向 虚函数表 ;

如果在 构造函数 中 调用 虚函数 , 则 没有 多态效果 ;





一、vptr 指针初始化问题




1、vptr 指针与虚函数表


" 虚函数表 " 由 C++ 编译器 负责 创建 与 维护 , 被 virtual 关键字 修饰的 虚函数 , 会自动 被 C++ 编译器 存储到 " 虚函数表 " 中 , 类中会自动添加一个 " vptr 指针 " 成员变量 指向 虚函数表 ;


2、vptr 指针初始化时机


对象中的 vptr 指针 指向 虚函数表 ,

在 对象 被 创建时 , 由 C++ 编译器 对 对象中的 vptr 指针进行初始化操作 ,

对象 创建完成 后 , 也就是 虚函数 整理完毕 , 全部放到 虚函数表 中后 ,

vptr 指针 才会指向 虚函数表 的首地址 ;


父类 对象 的 vptr 指针 指向 父类 的 虚函数表 首地址 ;

子类 对象 的 vptr 指针 指向 子类 的 虚函数表 首地址 ;


3、构造函数 中 调用 虚函数 - 没有多态效果


构造函数 的 作用就是 创建对象 ,

构造函数 最后 一行代码 执行完成 , 才意味着 对象构建完成 ,

对象构建完成后 , 才会将 vptr 指针 指向 虚函数表 ;


如果在 构造函数 中 调用 虚函数 , 则 没有 多态效果 ;

在 父类 的 构造函数中 , 调用了 父类的 虚函数 ;

此时 , 如果 创建 子类对象 , 执行 父类构造函数 , 仍然调用 父类 的虚函数 , 子类的虚函数 没有被调用 , 说明 构造函数 执行期间 , 多态没有生效 ;


参考 【C++】继承 ⑧ ( 继承 + 组合 模式的类对象 构造函数 和 析构函数 调用规则 ) 博客中的 构造函数 调用规则 :

  • 构造函数 : 父类 -> 成员 -> 自身 ;
    • 首先 , 调用 父类 构造函数 ;
    • 然后 , 调用 成员 构造函数 ; 也就是 成员变量 类型的 构造函数 ;
    • 最后 , 调用 自己 构造函数 ; 自身定义的 构造函数 ;
  • 析构函数 : 自身 -> 成员 -> 父类 ;
    • 首先 , 调用 自己 析构函数 ; 自身定义的 析构函数 ;
    • 然后 , 调用 成员 析构函数 ; 也就是 成员变量 类型的 析构函数 ;
    • 最后 , 调用 父类 析构函数 ;

4、代码示例


执行 Child c; 代码 , 创建 子类对象 ;

构造函数调用顺序是 父类 -> 成员 -> 自身 ;

首先 , 调用 父类 的 构造函数 , 然后再 父类构造函数 中调用 fun 虚函数 , 只能调用 父类本身的 fun 函数 , 此时 vptr 指针没有指向 虚函数表 , 虚函数表未生效 , 只能调用 父类的 fun 函数本身 ;

  • 父类的 构造函数 调用完毕后 , vptr 指针 才指向 父类的 虚函数表 ;

然后 , 调用 子类 的构造函数 , 此时在 子类构造函数 中调用 fun 虚函数 , 只能调用 子类本身的 fun 函数 , 此时 vptr 指针没有指向 虚函数表 , 虚函数表未生效 , 只能调用 子类的 fun 函数本身 ;

  • 子类的 构造函数 调用完毕后 , vptr 指针 才指向 子类的 虚函数表 ;

代码示例 :

#include "iostream"
using namespace std;// 父类
class Parent {
public:Parent(){cout << "调用父类构造函数" << endl;// 构造函数中调用父类的虚函数// 如果创建子类 , 此处调用的仍是 父类的 虚函数fun(1);}virtual void fun(int a){cout << "执行 父类 Parent 的 virtual void fun(int a) 函数" << endl;}
};class Child : public Parent
{
public:Child(){cout << "调用子类构造函数" << endl;// 构造函数中调用子类的虚函数// 如果创建子类 , 此处调用的仍是 子类的 虚函数 // 多态未生效fun(1);}virtual void fun(int a){cout << "执行 子类 Child 的 virtual void fun(int a) 函数" << endl;}
};int main() {Child c;// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
}

执行结果 :

调用父类构造函数
执行 父类 Parent 的 virtual void fun(int a) 函数
调用子类构造函数
执行 子类 Child 的 virtual void fun(int a) 函数
请按任意键继续. . .

在这里插入图片描述

这篇关于【C++】多态 ⑨ ( vptr 指针初始化问题 | 构造函数 中 调用 虚函数 - 没有多态效果 )的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/321898

相关文章

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

MySQL count()聚合函数详解

《MySQLcount()聚合函数详解》MySQL中的COUNT()函数,它是SQL中最常用的聚合函数之一,用于计算表中符合特定条件的行数,本文给大家介绍MySQLcount()聚合函数,感兴趣的朋... 目录核心功能语法形式重要特性与行为如何选择使用哪种形式?总结深入剖析一下 mysql 中的 COUNT

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MySQL 中 ROW_NUMBER() 函数最佳实践

《MySQL中ROW_NUMBER()函数最佳实践》MySQL中ROW_NUMBER()函数,作为窗口函数为每行分配唯一连续序号,区别于RANK()和DENSE_RANK(),特别适合分页、去重... 目录mysql 中 ROW_NUMBER() 函数详解一、基础语法二、核心特点三、典型应用场景1. 数据分

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决