C++学习笔记——virtual学习

virtual c++ 6.0 MFC教程 本书通过85个实例全面讲述了应用MFC进行Visual C++编程的思想。每个实例均以编写一个应用程序要走的步骤编写。全书共分四部分进行介绍,第一部分是基础知识,第二部分讲述用户界面的实例,第三部分讲述MFC内部处理方面的实例,第四部分讲述打包实例。全书基本上面向实例进行阐述,讲解透彻、易于掌握。本书既可作为初学者和大专院校师生的自学参考书,也可作为计算机软件开发人员的技术参考书。 立即下载

1、含义

        virtual关键字只用于类定义内部,出了类定义范围无法使用,即只能用于限定成员函数

        定义为virtual的函数是基类希望派生类重新定义的,否则不能声明为virtual函数。

        C++中的类成员函数默认为非virtual函数。

2、virtual与动态绑定

        通过动态绑定我们能够在编写程序时使用继承层次中的任意对象,无需关心对象的具体类型。使用这些类的程序无需关心函数是在基类还是在派生类中定义。

        在C++中通过基类的引用(或指针)调用虚函数时候,发生动态绑定,即引用(或指针)既可以是指向基类对象也可以是指向派生类对象。用引用(或指针)调用的虚函数在运行时确定被调用函数的实际定义。

        触发条件:

  • 只有指定为虚函数的成员函数才能进行动态绑定;
  • 必须通过基类类型的引用或指针进行函数调用;

3、virtual与派生类

        派生类一般会重新定义所有基类的虚函数(继承的),如果没有重新定义虚函数则默认使用基类中版本。

        派生类中对于虚函数的声明必须与基类中完全一样(但若返回类型是对基类的引用或指针的虚函数,在派生类中可以返回基类函数所返回类型的派生类应用或指针)。

        一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类可以保留virtual关键字。

4、virtual与构造、析构函数

        C++明确指出:当派生类对象通过基类指针被删除,而该基类有non-virtual析构函数,其结果未定义——实际执行时候该派生类的派生成分未被销毁,只销毁了继承自基类的部分。这就发生了诡异的“局部销毁”,形成资源泄露。因此:任何有多态性或者含有virtual函数的基类都必须包含有一个virtual析构函数。

        此外决不能在构造函数和析构函数中调用virtual函数,具体原因见参考文献【1】。

5、virtual与强制覆盖

        某些情况下希望覆盖虚函数,强调使用虚函数的特定版本,这是可以使用作用域操作符:

base *p_base = &dreived;
T  d = p_base->base_class::print_m();

这段代码强调对于func()的调用确定为base中定义的版本,这就使该调用在编译是确定。

6、pure virtual

       如果某个基类只是为了让其他类继承而不会有或者不应该有类对象,可以在类中定义纯虚函数(pure virtual function)。包含纯虚函数的类为抽象类,抽象类除了作为抽象基类进行派生,抽象类不能创建对象。

class base {
public:
        virtual void print_m() = 0;
}

上面定义的base为抽象类,func为纯虚函数。

7、示例

#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
using namespace std;


class base
{
public:
    virtual ~base(){
        cout<<__func__<<"\n";
    }
    virtual void print_m(){
        cout<<"IN Base: "<<__func__<<";Line "<<__LINE__<<"\n";
    }
};
//=======================================================================
class son1 : public base
{
public:
    son1(int mem):member(mem){ }
    ~son1() {
        cout<<__func__<<"\n";
    }
    void print_m() {
        cout<<"Here IN class son1,FUNC: "<<__func__<<" and its member="<<member<<"\n";
    }
private:
    int member;
};
//=======================================================================
class son2 : public base
{
public:
    son2(int mem):member(mem){ }
    ~son2() {
        cout<<__func__<<"\n";
    }
    void print_m() {
        cout<<"Here IN class son2,FUNC: "<<__func__<<" and its member="<<member<<"\n";
    }
private:
    int member;
};
//=======================================================================
class son3 : public base
{
public:
    son3(int mem):member(mem){ }
    ~son3() {
        cout<<__func__<<"\n";
    }
    void print_m() {
        cout<<"Here IN class son3,FUNC: "<<__func__<<" and its member="<<member<<"\n";
    }
private:
    int member;
};
//=======================================================================


base * get_base_ptr(int i, int mem=6)
{
    switch (i)
    {
        case 1  : return new son1(mem);
        case 2  : return new son2(mem);
        default : return new son3(mem);
    }
}


int main()
{
    std::vector<base*> v;
    for (int i = 1; i <= 3; ++i)
    {
        v.push_back(get_base_ptr(i,i*i*i));
    }
    
    BOOST_FOREACH(base * &b,v)
    {
        b->print_m();
        if(b!=NULL)
            delete b;
    }
    return 0;
}


/*****************执行结果*************************
Here IN class son1,FUNC: print_m and its member=1
~son1
~base
Here IN class son2,FUNC: print_m and its member=8
~son2
~base
Here IN class son3,FUNC: print_m and its member=27
~son3
~base
[Finished in 0.6s]
**************************************************/
并且如果去掉基类中析构函数的virtual输出结果为:

/*****************执行结果*************************
Here IN class son1,FUNC: print_m and its member=1
~base
Here IN class son2,FUNC: print_m and its member=8
~base
Here IN class son3,FUNC: print_m and its member=27
~base
[Finished in 0.6s]
**************************************************/
可以看出如果没有基类的虚析构函数,在通过基类指针销毁派生类对象时候未能完全销毁。


此时基类为抽象类,如果给该基类声明对象

base b;
则会出现以下错误:
/home/abing/software/clang_tool/test.cc: 在函数‘int main()’中:
/home/abing/software/clang_tool/test.cc:74:10: 错误: 不能将变量‘b’声明为具有抽象类型‘base’
/home/abing/software/clang_tool/test.cc:6:7: 附注:   因为下列虚函数在‘base’中为纯虚函数:
/home/abing/software/clang_tool/test.cc:15:18: 附注: 	virtual void base::print_m()
[Finished in 0.4s with exit code 1]


如果将基类中的:

virtual void print_m() = 0 ;

改为:

virtual void print_m(){
        cout<<"IN Base: "<<__func__<<";Line "<<__LINE__<<"\n";
    }
并且将

BOOST_FOREACH(base * &b,v)
    {
        b->print_m();
        if(b!=NULL)
            delete b;
    }
改为:

BOOST_FOREACH(base * &b,v)
    {
        b->base::print_m();
        if(b!=NULL)
            delete b;
    }
则执行结果为:

/*****************执行结果*************************
IN Base: print_m;Line 13
~son1
~base
IN Base: print_m;Line 13
~son2
~base
IN Base: print_m;Line 13
~son3
~base
[Finished in 0.6s]
**************************************************/
8、参考文献

[1] 《C++ Primer》第四版

[2] 《Effective C++》第三版
注:转载请说明出处http://blog.youkuaiyun.com/abingzhao/article/details/25548263










MDB协议中设备级别和选项(Levels and Options) MDB协议的LEVEL级别介绍 阅读详情

相关推荐

1.毫米波雷达心率、呼吸原理实现(一)实例

毫米波雷达心率、呼吸检测原理本项目分两到三篇文章写完,第一阶段借鉴TI开源项目以及根据自己的见解适当更改信号处理链通过AWR1843汽车雷达传感器和DCA1000采集卡采集数据完成人体呼吸和心跳检测算法的实现。第二阶段将仿真成功的代码搬移TIAWR1843传感器并通过串口数据实现生命体征的实时处理。本文即为第一阶段实现过程。本文首先概述毫米波雷达呼吸心跳检测的原理,紧接着概述本项目信号处理流程,另外本项目第一阶段通过matlab仿真验证算法的有效性,仿真源码可关注小编并私信可取。1.概述。

Poulen的博客 2万+

C++静态多态和动态多态(静态联编:函数地址早绑定、动态联编:函数地址晚绑定)

如果没有使用虚函数,即没有利用 C++ 多态性,则利用基类指针调用相应函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写的函数。当我们使用delete删除一个指向子类对象的基类指针时,如果基类的析构函数不是虚函数,那么只会调用基类的析构函数,而不会调用子类的析构函数,这样就会导致子类对象的资源没有被释放,从而引发内存泄漏等问题。动态多态最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而调用不同的方法。而多态的目的则是为了。

Dontla的博客 2701

计算机组成原理实验:手把手教你用模拟程序实现中断机制(附完整代码)

本文通过一个计算机组成原理模拟实验,手把手教你从零实现一个可工作的中断系统。实验详细演示了如何配置中断控制器、编写主程序与中断服务程序,并观察中断响应、现场保护和恢复的全过程,深入解析了中断原理的核心机制。

471

C++的动态绑定(Boolan笔记第五周)

C++的动态绑定

roufoo的博客 2355

西电微原课设——矩阵式键盘数字密码锁设计

内容要求:根据设定好的密码,采用4x4矩阵键盘实现密码的输入功能。当密码输入正确之后,锁就打开(绿灯亮),10秒之后,锁自动关闭(红灯亮);如果连续输入三次密码不正确,就锁定按键5秒钟,同时发出报警(黄灯闪),5秒后,解除按键锁定,恢复密码输入。...

Polaris的博客 1万+

同花顺CM函数深度解析:如何精准设置历史换手衰减系数以优化筹码峰

本文深度解析同花顺CM函数中的历史换手衰减系数,揭示其如何影响筹码峰准确性。通过拆解CM函数参数,并提供基于前十大流通股东锁定比例的计算公式,指导用户精准设置该系数,从而优化筹码峰模型,使其更真实反映个股持仓成本分布,提升技术分析的有效性。

mango的博客 1778

C++三大特性—多态 “虚函数与动态绑定”

多态的定义及实现、虚函数重写、动态绑定、override和final (C++11)、重载、覆盖(重写)、隐藏(重定义)的对比、成员函数与继承、派生类中的虚函数虚函数与默认实参、如何回避虚函数的机制

DEXTERFUTIAN的博客 2927

AI驱动Spine骨骼动画生成:从图像拆分到动画自动化的全流程解析

计算机视觉与生成式AI技术正深度赋能传统数字内容生产流程。在2D动画制作领域,骨骼动画技术通过模拟真实生物关节运动,实现了角色动画的高效制作与灵活控制。其核心原理在于将角色拆分为独立部件,通过骨骼层级关系驱动部件变换,结合关键帧插值算法生成流畅动作。这项技术的工程价值在于大幅降低动画制作门槛,提升内容生产效率,尤其适用于游戏开发、影视预演、广告设计等需要快速原型迭代的场景。本文聚焦于如何利用SIFT特征匹配、RANSAC几何验证等CV算法实现部件自动对位,并结合动画12原则的程序化实现,构建从单张角色立绘到

weixin_28696185的博客 1517

C++ 虚函数详解(动态绑定)

虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。我们在使用基类的引用(指针)调用虚函数时,就会发生动态绑定。所谓动态绑定,就是在运行时,虚函数会根据绑定对象的实际类型,选择调用函数的版本。

qq_41897304的博客 1216

SAP ABAP 实用小技巧合集 (一)

本合集包含了笔者从事 ABAP 开发 17 年以来积累的小技巧。持续更新。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 3217

TI硬件笔试面试题型解析上

摘要: 本专栏聚焦大厂硬件笔试面试题解析,当前第14期以德州仪器(TI)为例,涵盖模拟电路与数字电路核心考点。模拟电路部分涉及最大功率传递定理、运放设计(虚短/虚断)、共模抑制比(CMRR)及高频PCB信号完整性;数字电路包括CMOS与TTL对比、建立/保持时间、竞争冒险消除、D触发器分频及“线与”逻辑实现。文章强调通过例题掌握解题方法与知识点,助力读者应对硬件工程岗位的笔试与面试挑战,附常见考点总结与实用建议。全文注重理论与实践结合,提供典型题型解析与设计思路。(150字)

启芯硬件的博客 950

【C/C++ 基础知识 】C++中的静态绑定与动态绑定:深入解析与比较

绑定,简而言之,是将一个名称(如变量、函数等)与其所在的内存地址关联起来。这种关联可以在编译时(静态)或运行时(动态)确定。

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 2004

对接金蝶云星空调用即时库存信息查询API(附JAVA实现)

这篇文章教你如何对接金蝶API并且使用JAVA调用金蝶云星空的即时库存查询API

实用的博客 2608

C++学习笔记-virtual method

C++学习笔记-virtual method 虚函数或虚方法(virtual method)在CPP13章第3节首次提到。 在通过引用或指针而不是直接用对象调用方法时,如果基类的方法没有使用关键词 virtual ,程序将根据引用类型或指针类型选择方法;如果使用了virtual,程序将根据引用或指针指向的对象类型来选择方法。 通俗来讲,如果要在派生类中重新定义基类方法的话,最好在基类中定义为虚...

weixin_30486037的博客 340

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C++11 virtual函数学习笔记

#include #include using namespace std; class Base { public: Base(){} ~Base(){} public: virtual void f1(int x){ cout "baseclass: f1() " x << endl; } virtual void f2()final{ cout "baseclass: f2()  "

AdairJun 1159

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

c++学习笔记:virtual constructor

使用子类创建对象,并给父类指针赋值时,当调用delete语句时,只会调用父类的析构函数。这样,如果在子类的析构函数中有数据操作,就可能发生内存泄漏。 #include <iostream> class Base { public: Base() { std::cout << "Base Constructor\n"; } ~Base() { std::cout << "Base Destructor\n"; } }; class Derived :

luobaotai的博客 528

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【C++快速上手】九、virtual学习笔记

结论 虚函数的调用取决于指向或者引用的对象的类型,而不是指针或者引用自身的类型。 默认参数是静态绑定的,虚函数是动态绑定的。 默认参数的使用需要看指针或者引用本身的类型,而不是对象的类型。 1、虚函数与运行多态 虚函数的调用取决于指向或者引用的对象的类型,而不是指针或者引用自身的类型。 #include<iostream> using namespace std; class Employee { public: virtual void raiseSalary() {

悟已往之不谏 知来者之可追 774

C++ 学习笔记 1 - Virtual

三种主要用法: 1. 多态 - 解决的问题 在面向对象的编程中,首先会针对数据进行抽象(确定基类)和继承(确定派生类),构成类层次。这个类层次的使用者在使用它们的时候,如果仍然在需要基类的时候写针对基类的代码,在需要派生类的时候写针对派生类的代码,就等于类层次完全暴露在使用者面前。如果这个类层次有任何的改变(增加了新类),都需要使用者“知道”(针对新类写代码)。这样就增加了类层次与其使用者之间的耦合,有人把这种情况列为程序中的“bad smell”之一。 - 如何实现 编译器是如何针对虚函数产生可

weixin_43629008的博客 100

C++学习笔记:关键字virtual 虚继承

C++学习笔记:关键字virtual 虚继承 (依旧是程设2的笔记) 普通继承与虚继承,只有在派生类重复继承某一基类时,才有区别。virtual可以使派生类在重复继承一个基类时,只产生唯一的一份基类副本。如上图,当B,C的继承代码为: class B:virtual public A{}; class C: virtual public A{}; 此时D只会继承一份A,而不会通过B,C重复继承了。此时A即为虚基类。 那么虚继承的本质是什么呢? 给出了以下代码: #include<iostream&g

qq_45779361的博客 505

C++学习笔记:Virtual的使用心得

Virtual是C++中实现多态的关键所在。 Virtual使用的地方有两个地方: 1、类与类之间的虚继承。(主要用于在多继承中出现二义性时) 首先上第一个用处的测试代码: #include <iostream> using namespace std; class A { public: A(){cout<<"A con...

weixin_33955681的博客 117

C++学习笔记25,析构函数总是会宣布virtual

为了永远记住析构函数声明virtual----&gt;&lt;&lt;effective c++&gt;&gt; 为这句话不一定对,但无需质疑的是这句话是非常实用的. 查看以下的样例: #include &lt;iostream&gt; #include &lt;string&gt; using namespace std; class B{ public: ~B(){ cou...

weixin_34347651的博客 83

C++ 学习笔记 (七)继承与多态 virtual关键字的使用场景

  在上一篇C++ 学习笔记 (六) 继承- 子类与父类有同名函数,变量中说了当父类子类有同名函数时在外部调用时如果不加父类名则会默认调用子类的函数。C++有函数重写的功能需要添加virtual关键字,当时写上篇博文的时候在想那么既然默认使用子类的同名函数那么这个virtual有什么意义呢? 这篇博文主要讲述 多态:一句相同的语句能有不同的实现。      当父类和子类有同名函...

argentina1111的博客 308

C++学习笔记25,永远将析构函数声明为virtual

要永远记得将析构函数声明为virtual---->> 或许你觉得这句话不一定对,但无需质疑的是这句话是很有用的. 查看下面的例子: #include #include using namespace std; class B{ public: ~B(){ cout<<"base is destroyed!"<<endl; } }; class D:public B{ public

roaylchen的博客 1268
上一篇: C语言的整型溢出问题
下一篇: ubuntu中sublime配置
abing0513
博客等级 码龄16年 25粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值