IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 实用经验 86 绝对不要重新定义继承而来的缺省参数 -> 正文阅读

[网络协议]实用经验 86 绝对不要重新定义继承而来的缺省参数

virtual函数的缺省参数,是C++语言的一种语法糖。即使有多年编程经验的老程序员,也不免在此跌跟头。发生此类现象最根本的问题是:没有理解虚函数采用动态绑定策略,而缺省参数却采用的是静态绑定策略。

对象的所谓静态类型,是它在程序中被声明时所采用的类型。函数的默认参数就采用静态绑定,就是指在程序运行的过程中不会发生变化。

// 流量实现基类
class CStreamBase
{
public:
   virtual SendStream(int iStreamLength = 1500);
};

//  TCP流量
class CTCPStream: public CStreamBase
{
public:
    virtual SendStream(int iStreamLength = 1460);
};

//  UDP流量
class CUDPStream: public CStreamBase
{
public:
    virtual SendStream(int iStreamLength = 1460);
};

// pStream的静态类型是CStreamBase *
CStreamBase *pStream = NULL; 
// pTCPStream的静态类型是CStreamBase *
CStreamBase *pTCPStream = new CTCPStream();  
// pUDPStream的静态类型是CStreamBase *
CStreamBase *pUDPStream = new CUDPStream ();  

所谓动态类型是指“目前所指对象的类型”。动态类型可以在程序执行过程中变化。虚函数就采用这种机制,如果你调用一个虚函数,究竟调用哪分函数代码实现,这取决于发生函数调用的那个动态类型。

// 调用CTCPStream:: SendStream()
pTCPStream->SendStream(1000);
// 调用CUDPStream:: SendStream()
pUDPStream-> SendStream(1000); 

那么,两种结合呢?我们看两者结合的结果。带有缺省参数的virtual函数,由于virtual函数是动态绑定,而缺省参数是静态绑定。这种实现真正的意义是:“调用一个定义于派生类内的virtual函数”的同时,而使用基类为其指定的缺省参数值。

//调用调用CUDPStream:: SendStream(1500)!
pUDPStream -> SendStream (); 

pUDPStream的动态类型是CUDPStream*,所以调用的是CUDPStream的virtual函数。CUDPStream:: SendStream函数的缺省参数应该是1500,但由于pUDPStream的静态类型是CStreamBase *,所以调用的缺省参数值来自CStreamBase class而非CUDPStreamclass!这种问题对于引用依然存在。

这种实现规则确实让我们有些惊讶,但是为什么C++会采用这种奇怪的实现策略呢?答案在于运行期效率。如果缺省参数是动态绑定的,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值。这比目前实行的“在编译期决定”的机制更慢并且复杂。为了程序的执行速度和编译器实现上的简易度,c++做了这样的取舍,其结果就是你如今享受的执行效率。

带缺省参数的virtual函数

  • 带缺省参数的virtual函数实现时,虚函数采用动态绑定,缺省参数采用静态绑定。在基类虚函数中声明的确省参数,即使派生类中重新指定了缺省参数,在多态调用时,依然采用基类虚函数中的缺省参数。
  • C++采用这种奇怪的实现机制,主要是从实现效率的角度考虑的。

关于virtual函数的缺省参数的运行原理,我们彻底明白了。现在的问题是如果避免坠入陷阱。

有人提过这样一个策略:试图要求所有的派生类类型的设计工程师,在继承类实现时精确的复制基类中的虚函数参数默认值 ,已解决此类陷阱问题。实践证明这是一个糟糕的主意。

(1)未必所有的工程师都遵从这个要求,有些人也许根本不把此约定当回事。

(2)这个建议,没有考虑到(基类类型变化)不可预知的因素。其将派生类置于“基类类型变化”的危卵上。一旦虚函数的缺省参数发生变化了。这就势必造成所以派生类都要做相应的变化。对于一个大型工程,这种变化是一个浩大的工程。

(3)没有考虑到缺省参数可能发生变化。这种变化一般与基类和派生类的作用域有关。看下面这个例子。你很难指责派生类的设计人员使用了错误的MAX_STREAM_LENGTH。

const  int  MAX_STREAM_LENGTH = 1500;

namespace STREAM
 {
// 流量实现基类
class CStreamBase
{
public:
   // 此处的MAX_STREAM_LENGTH = 1500
   virtual SendStream(int iStreamLength = MAX_STREAM_LENGTH);
};
}

namespace STREAM
{
const  int  MAX_STREAM_LENGTH = 1460;
//  TCP流量
class CTCPStream: public CStreamBase
{
public:
// 此处的MAX_STREAM_LENGTH = 1460
virtual SendStream(int iStreamLength = MAX_STREAM_LENGTH); 
};

//  UDP流量
class CUDPStream: public CStreamBase
{
public: 
// 此处的MAX_STREAM_LENGTH = 1460
virtual SendStream(int iStreamLength = MAX_STREAM_LENGTH); 
}
}

其实,问题还远非这些。最简单的避免方案就是避免在虚函数中提供参数的缺省初始化。考虑替代方案。NVI替代方案就是一种较好的替代方案:令基类的一个public non-virtual函数调用一个private virtual函数,后者可被派生类重新定义。可让non-virtual函数指定缺省参数,而private virtual函数负责真正实现工作。

// 流量实现基类
class CStreamBase
{
public:
   void SendStream(int iStreamLength = MAX_STREAM_LENGTH)
   {
      SendStreamImpl(iStreamLength);
   }
   virtual SendStreamImpl(int iStreamLength);
};

//  TCP流量
class CTCPStream: public CStreamBase
{
public:
    virtual SendStreamImpl(int iStreamLength);
};

//  UDP流量
class CUDPStream: public CStreamBase
{
public:
   virtual SendStreamImpl(int iStreamLength);
};

采用NVI这种方式,用户可采用静态的方式从基类类型的接口取得参数默认值,而派生类也可自由的变更函数的行为,而不用担心什么缺省初始化物了。

请谨记

  • 绝对不要重新定义继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数——你唯一应该覆写的东西——却是动态绑定。
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-10-02 15:11:36  更:2021-10-02 15:12:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年6日历 -2024/6/29 18:32:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码