c++之默认参数的可继承性及其原理

        关于c++默认参数是否可以继承这个问题。我们先给出答案是,不可继承并且静态绑定为当前类接口的默认参数(如果有声明的话)上。当然,我们不单只需要知道结果,还应该探索一下原因。

        考虑以下代码。非常简单的一段测试代码。有个IServer基类,其listen接口有个默认参数。IServer派生出两个子类CRPCServer与CHTTPServer,两个子类在其listen接口上绑定不同的默认参数。

#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

enum
{
	BACKLOG_DEFULT = 128,
	BACKLOG_MEDIUM = 1024,
	BACKLOG_BIGGER = 2048
};
class IServer
{
	public:
		virtual ~IServer(){};
		virtual int listen(int backlog = BACKLOG_DEFULT) = 0;
};
class CRPCServer: public IServer
{
	public:
		~CRPCServer(){};
		int listen(int backlog = BACKLOG_MEDIUM)
		{
			printf("IRPCServer listen backlog: %d\n", backlog);
			return 0;
		}
};
class CHTTPServer: public IServer
{
	public:
		~CHTTPServer(){};
		int listen(int backlog = BACKLOG_BIGGER)
		{
			printf("CHTTPServer listen backlog: %d\n", backlog);
			return 0;
		}
};

int main(int argc, char *argv[])
{
	IServer *irpcserver = new CRPCServer;
	IServer *ihttpserver = new CHTTPServer;

	irpcserver->listen();
	ihttpserver->listen(0x1000);
	
	delete irpcserver; irpcserver = NULL;
	delete ihttpserver; ihttpserver = NULL;
	
	return 0;
}

        执行结果如下,可见irpcserver虽然真正指向对象类型是CRPCServer。但是其listen接口默认参数是绑定到IServer::listen的默认参数上的。也就是说默认参数不具备多态,也不具备可继承性。它是通过编译器静态绑定到当前对象的接口上的默认参数。倘若要实现多态,那么其中普遍做法就是放入虚表中,那么复杂性以及管理又会变得复杂。

IRPCServer listen backlog: 128
CHTTPServer listen backlog: 4096

         接下来我们来看默认参数,编译器是怎么处理的。部分分析涉及多态原理,可以参考另外一篇博客<深入理解c++多态实现原理>。我们对main函数反汇编后提取两次调用listen来看。可见irpcserver调用listen时是将128入栈传参,即绑定到IServer::listen的默认参数。ihttpserver调用listen时则传入了我们指定的非默认参数。

Dump of assembler code for function main(int, char**):
   ...
   0x080485c2 <+62>:    mov    0x18(%esp),%eax          # 将irpcserver放入eax寄存器
   0x080485c6 <+66>:    mov    (%eax),%eax              # 取出irpcserver的vptr虚表指针
   0x080485c8 <+68>:    add    $0x8,%eax                # 偏移掉vptr中两个析构函数
   0x080485cb <+71>:    mov    (%eax),%edx              # 取出虚表中irpcserver函数地址
   0x080485cd <+73>:    movl   $0x80,0x4(%esp)          # 0x80(128)入栈
   0x080485d5 <+81>:    mov    0x18(%esp),%eax          
   0x080485d9 <+85>:    mov    %eax,(%esp)              # irpcserver指针入栈
   0x080485dc <+88>:    call   *%edx                    # 调用listen函数
   
   0x080485de <+90>:    mov    0x1c(%esp),%eax          # 将ihttpserver放入eax寄存器
   0x080485e2 <+94>:    mov    (%eax),%eax              # 取出ihttpserver的vptr虚表指针
   0x080485e4 <+96>:    add    $0x8,%eax                # 偏移掉vptr中两个析构函数
   0x080485e7 <+99>:    mov    (%eax),%edx              # 取出虚表中listen函数地址
   0x080485e9 <+101>:   movl   $0x1000,0x4(%esp)        # 0x1000入栈
   0x080485f1 <+109>:   mov    0x1c(%esp),%eax          
   0x080485f5 <+113>:   mov    %eax,(%esp)              # ihttpserver指针入栈
   0x080485f8 <+116>:   call   *%edx                    # 调用listen函数
   ...

总结:

1)  默认参数不可继承,也不具备多态,其绑定到当前对象接口的默认参数上

2) 倘若要加入默认参数,最好基类与子类保持一致,避免看起来传入不同默认参数的怪异的行为。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值