《认清C++语言》---继承而来的非虚函数和缺省参数

C++缺省参数 如果函数的声明和定义分开,缺省参数只能在函数声明中指定,不能在函数定义中重复指定,否则会导致编译错误。示例如下:// 函数声明,指定缺省参数// 函数定义,不需要再次指定缺省参数。 阅读详情

======================继承而来的非虚函数======================

假设类Derive公有继承自类Base,且类Base定义了一个公有非虚成员函数func:

class Base

{

public:

         void Func();

         ...

};

 

class Derive : public Base

{

         ...

}

Derive de;       //定义一个派生类对象

Base *pb = &de;    //得到一个指向de的Base指针

pb->Func();   //通过指针调用Func()

 

Derive *pd = &de;         //得到一个指向de的Derive指针

pd->Func();   //通过指针调用Func()

 

这种情况下,如果Derive没有重新定义Func函数时,两个指针调用Func的行为是相同的;但是,如果Func是非虚函数且Derive又重新定义了自己的Func版本,那么两者的行为就不会相同了:

class Derive : public Base

{

public:

         void Func();   //隐藏了Base::Func;

         ...

};

 

pb->Func();   //调用Base::Func()

pd->Func();   //调用Derive::Func()

这种行为的两面性原因在于:Base::Func()和Derive::Func()这样的非虚函数是静态绑定的。因此,即使pb指向的是从Base派生的类的对象,但由于pb被声明为执行Base的指针类型,所以通过pb调用非虚函数时就总是调用那些定义在类Base中的函数。

与之相反,虚函数是动态绑定的,不会存在上述问题。通过pb或pd调用Func()时都将导致调用Derive::Func(),因为pb和pd实际上都是指向类型Derive的对象。

 

结论:如果写派生类Derive时重新定义从基类Base继承而来的非虚函数Func(),Derive的对象就可能表现出精神分裂的症状。引用也会和指针一样表现出这种异常行为。

因此,任何情况下都要禁止重新定义继承而来的非虚函数。

 

======================继承而来的缺省参数值=====================

重定义缺省参数值的唯一方法是重定义一个继承而来的函数,而由上面分析可知,重定义继承而来的非虚函数是错误的,因此,我们这里主要讨论“继承一个有缺省参数值的虚函数”。

 

虚函数是动态绑定的而缺省参数值是静态绑定的。

 

对象的静态类型是指我们声明的存在于程序代码文本中的类型:

enum ASCEShapeColor{RED, GREEN, BLUE};

class ASCEShape     //基类

{

public:

         //所有形状都要提供这样一个函数绘制自身

         virtual void draw(ASCEShapeColor color=RED) const = 0;

         ...

};

 

class ASCERectangle : public ASCEShape

{

public:

         //定义了不同的缺省参数值错误!

         virtual void draw(ASCEShapeColor color = GREEN) const;

         ...

};

 

class ASCECircle : public ASCEShape

{

public:

         virtual void draw(ASCEShapeColor color) const;

         ...

};

 

ASCEShape *ps;      //静态类型是ASCEShape*

ASCEShape *pr = new ASCEShapeRectangle;    // 静态类型是ASCEShape*

ASCEShape *pc = new ASCEShapeCircle;  //静态类型是ASCEShape*

上面三个指针的静态类型和它们实际所指向的对象的类型是没有关系的。

 

而对象的动态类型是由它当前所指的对象的类型决定的,即对象的动态类型表示它将执行何种行为。如上面的代码中,pr的动态类型是ASCERectangle*,pc的动态类型是ASCECircle*,而ps实际上没有动态类型,因为它还没有指向任何对象。

动态类型可以在程序运行时改变,典型的方法是通过赋值:

ps = pr;   //ps的动态类型现在是ASCERectangle*

ps = pc;  //ps的动态类型现在是ASCECircle*

 

虚函数就是动态绑定的,即虚函数通过哪个对象被调用,具体被调用的函数就由那个对象的动态类型决定:

pr->draw(REG);    //调用ASCERectangle::draw(REG)

pc->draw(REG);   //调用ASCECircle::draw(REG)

虚函数机制的正确性是毋庸置疑的,但当将虚函数和缺省参数值结合起来分析就会产生问题:虚函数是动态绑定的,但缺省参数是静态绑定的,这意味着我们最终可能调用的是一个定义在派生类,但使用了基类中的缺省参数值的虚函数:

pr->draw();   //调用的是ASCERectangle::draw(REG)!!,而不是ASCERectangle::draw(GREEN)

 

由此,结论就是:禁止重新定义继承而来的缺省参数值!!

 

 

不要重新定义继承来的非虚函数 –effective c++ item 36首先考虑下面的代码class B { public: void foo() { std::cout << "test_B\n"; } };class D: public B { public: void foo() { std::cout << "test_D\n"; } }; 阅读详情

相关推荐

C++中的继承(不包含虚函数)

什么是继承? ●继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生的类,称为派生类。 怎样实现继承? 继承权限 继承权限分为public、protected、private三种权限。 ●protected权限专为继承而生,如果你希望你定义的类中的成员可以被它的派生类访问,同时不能在类外被直接访问,就定义为pro...

h___q的博客 1231

C++ 重新定义继承而来的非虚函数

之前在Effective C++看到有一个条款:决不要重新定义继承而来的非虚函数,今天在看代码的时候,发现有人重新定义继承而来的非虚函数,所以重新查看了这个条款。虽然这样做(重新定义继承而来的非虚函数)不会出什么大错,只要注意声明的指针即可。但是我觉得还是不合理。先看例子。我把Effective C++例子补全如下:#include "stdafx.h"#include "stdlib.h"#inc

一首小夜曲 5153

技术解析:ChangeFormer——基于Transformer的孪生网络在遥感变化检测中的创新应用

本文深入解析了ChangeFormer模型在遥感变化检测中的创新应用,该模型基于Transformer架构和孪生网络(Siamese Network),显著提升了变化检测的精度和效率。通过详细的技术实现和实战经验分享,展示了其在建筑物变化、森林监测等场景中的卓越表现,为相关领域的研究和应用提供了有力工具。

weixin_29102619的博客 495

c++ default parameter demo

// from essential c++ chapter 3#include #include #include #include using namespace std; void display(const vector &ivec, ostream &os = cout); int main() { int arr[] = {1, 2, 3, 4}; size_t arr

\s 636

从零到一:3D-ResNet50在医学影像中的实战陷阱与避坑指南

本文深入探讨了3D-ResNet50在医学影像分类中的实战应用,重点解析了NIFTI数据预处理、小样本迁移学习陷阱及GPU显存优化等关键问题。通过分块加载、混合精度训练等技巧,有效解决了医学影像处理中的内存管理和性能挑战,为开发者提供了实用的避坑指南和优化策略。

weixin_29038345的博客 390

从像素到光影:解析《黑神话:悟空》中Nanite与Lumen的协同渲染革命

本文深度解析了《黑神话:悟空》如何利用Unreal Engine 5的Nanite与Lumen技术实现渲染革命。Nanite处理亿级多边形几何细节,无需传统LOD优化;Lumen提供全动态全局光照,实现实时光线反弹。两者协同工作,为游戏带来电影级视觉体验,重塑了实时渲染的技术标准与工作流程。

f6g7h8i9j的博客 425

C++笔记5--默认参数(default parameters)

C++笔记4--默认参数(default parameters)                                                                                                                               声明:本文档由王桂林老师编写的C++课件,经整理而出。 通常情况下,函

黑土地 专栏 1390

U8+ 新建账套

一、登录 账套主管:demo 对账套有修改权限,有账套库权限 系统管理员:admin 其他所有权限 二、新建账套 admin登录–>点击系统管理–>账套–>建立 选择了参照已有账套结转期初:就是上一个的期末数据作为这一次新表的期初数据 启用会计期一旦选择更改不了。 ...

茶茶很困i 3693

C++继承中虚方法和非虚方法的调用问题

碰到一道填空的编程题。已有的程序 #include <iostream> using namespace std; class A { }; class B: public A { void Do() {cout << "B::Do" << endl;} }; class C : public B { public: void Do() {cout << "C::Do" << endl;} void Fun() {cout <

qq_36346625的博客 825

Qt实现仿微信在线聊天工具(服务器、客户端)V1_ 01

在主控类中绑定登录类的信号 void signalLoginOK() ,跳过验证步骤,先实现页面的跳转MyChatToolClient.h文件//登录成功槽函数 private :MyChatToolClient.cpp文件} //登录成功槽函数 void MyChatToolClient :: slotLoginOk() {//关闭登录窗口并释放空间 m_loginWin -> close();//新建并打开聊天窗口 m_chatWin = new ChatWindow;

Leo的博客 1870

AI Olympics:面向真实决策的不完全信息博弈评测框架

在人工智能落地实践中,传统基准测试(如MMLU、GSM8K)擅长评估知识调用与静态推理能力,却难以衡量模型在动态、模糊、高代价的真实场景中的策略决策水平。AI Olympics正是为弥补这一断层而生——它以扑克、狼人杀等社会性博弈为载体,系统考察模型在不完全信息下的对手建模、欺骗检测、多步序贯推理与代价敏感执行等核心能力。其技术价值在于将‘黑箱决策’转化为可追踪、可归因、可压力测试的动态思维图谱(DTG),支撑金融风控、智能客服、交通调度等强交互业务场景的能力诊断与持续优化。

weixin_30983965的博客 296

C++中虚函数和非虚函数重载在继承时的区别

这里唯一想说明的一点就是,使用虚函数继承时,当继承类被强转成基类后调用虚函数,调用的还是继承类的虚函数。而重载方式的继承类被强转成基类再调用重载函数,则调用的是基类的函数。废话不多说,上代码: [cpp] view plaincopy #include    using namespace std;   class A {   public

imxiangzi的专栏 929

Java电商秒杀系统性能优化(一)——电商秒杀系统框架回顾

电商秒杀系统框架回顾项目简介外部依赖框架回顾项目要点项目中存在的问题小结 课程是免费的,课程地址如下:SpringBoot搭建电商秒杀项目,课程真的很棒,作者的思路很清晰,建议各位读者可以跟着视频练习一下这个项目; 项目简介 通过SpringBoot快速搭建的前后端分离的电商基础秒杀项目。项目通过应用领域驱动型的分层模型设计方式去完成:用户otp注册、登陆、查看、商品列表、进入商品详情以及倒计时秒...

ghw15221836342的博客 3718

实用经验 85 不要重新定义继承而来的非虚函数

重定义继承而来的非虚函数导致的最明显的现象就是函数覆盖。任何情况下都要禁止重新定义继承而来的非虚函数。以防产生函数覆盖现象。

一鸣惊人的博客 513

Sparse-ICP—(4) 加权稀疏迭代最近点算法(matlab版)

本文介绍了加权稀疏迭代最近点算法的实现。

点云侠的博客 642

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

C++之继承初识(不包含虚拟继承)

C++是一种面向对象的语言,而面向对象,有着三大特征——封装,继承,多态。 关于封装,在我的其它博客中已经有过简单的介绍了。这里我将简单叙述一下面向对象的三大特征之二——继承。 什么是继承 继承机制是面向对象程序设计中,使代码复用的最重要手段,它允许设计者在保持原有类的特性的基础上进行扩展,增加功能。 通过这种方法产生的新的类,被称为派生类;被继承的类。则称为派生类的基类。 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。 继承是类设计层次的复用。 继承的定义格式.

qq_56713382的博客 527

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

c++重载函数和虚函数中的默认(缺省)参数

在上面的代码中,由于第一个函数带有缺省值,在函数调用时无法确定调用哪一个,导致调用的二义性。(因此我们应该避免在重载函数中使用默认参数,这很容易导致二义性)。最近在看公司的c++编码规范中,提到了重载函数和虚函数的默认参数的注意事项,这些内容有些印象但不是太清楚了,现在写一写算是复习了。

mingwu96的博客 649

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Effective C++ 2e Item37

条款37: 决不要重新定义继承而来的非虚函数有两种方法来看待这个问题:理论的方法和实践的方法。让我们先从实践的方法开始。毕竟,理论家一般都很耐心。假设类D公有继承于类B,并且类B中定义了一个公有成员函数mf。mf的参数和返回类型不重要,所以假设都为void。换句话说,我这么写:class B {public:  void mf();  ...};class D: public B

lostmouse的专栏 875

【C++】继承(二)多继承,虚函数,虚继承

1.单继承与多继承 2.菱形继承 3.虚继承---解决菱形继承的二义性和数据冗余的问题 4.虚函数与多态 5.继承体系同名函数的关系   1.单继承与多继承  1.1单继承:       一个子类只有一个直接父类。   class Person class Student : public Person class Monitor : public Student  1.2多继...

hgelin的博客 7278

C++继承、虚函数处的面试题

昨天,收到 SenseTime公司面试官的电话面试(一天面了三家公司,收获挺多的),通话时间将近1个半小时,面试过程中暴露出很多知识上的漏洞,本篇文章针对面试过程中继承以及虚函数方面的知识做一总结,查缺补漏,希望多大家有帮助。 单继承下的虚函数表 //单继承下虚函数表:是如何组织的 class A{ public: virtual void func(){ cout << "A::fun

zyh_helen的专栏 8708
上一篇: 《认清C++语言》---接口继承和实现继承
下一篇: 《Windows核心编程》---邮槽通信
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值