动态网站制作指南 [  QQ表情  ]
[ 投票调查 ]
[ 企业邮箱 ]
[ 网站空间 ]
网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
ASP源码 | .Net源码 | PHP源码 | JSP源码 | JAVA源码 | CGI源码 | VB源码 | C++源码 | Delphi源码 | PB源码 | VF源码 | 汇编 | 服务器
电脑书籍下载:程序设计书籍 | 数据库教程书籍 | 平面与多媒体书籍 | 网络通讯书籍 | 系统管理书籍 | 网络安全书籍 | 认证考试书籍
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > C/C++教程
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
文章搜索服务
邮件订阅
输入你的邮件地址,
你将不会错过任何关于:
[ C/C++教程 ]的信息



本月文章推荐
.C语言中的面向对象(1)-类模拟和.
.VC++编程隐藏计算机中的鼠标.
.递归找迷宫.
.命令行参数的分析.
.VC打造自己特色的屏幕保护.
.More Effective C++:理解new和d.
.全屏幕编辑软件的编写(C语言)04.
.让C/C++图形程序独立运行.
.我的OLEDB SqlHelper.
.C++数据结构学习:用栈做表达式求.
.C++辨析系列谈(二).
.c++面向对象的编程入门篇-----类.
.我编的国旗图形.
.玩转BCB的IDE,体验ToolsAPI.
.制作固定大小的Form.
.TList的用法.
.设备驱动.
.C++ STL编程轻松入门.
.挑战30天C++入门极限-引 言.
.C语言基础教程(一)基础篇.

C++箴言:访问模板化基类中名字

发表日期:2008-3-8 |



  假设我们要写一个应用程序,它可以把消息传送到几个不同的公司去。消息既可以以加密方式也可以以明文(不加密)的方式传送。假如我们有足够的信息在编译期间确定哪个消息将要发送给哪个公司,我们就可以用一个 template-based(模板基)来解决问题:

  class CompanyA {
  public:
  ...
  void sendCleartext(const std::string& msg);
  void sendEncrypted(const std::string& msg);
  ...
  };

  class CompanyB {
  public:
  ...
  void sendCleartext(const std::string& msg);
  void sendEncrypted(const std::string& msg);
  ...
  };
  ... // classes for other companies

  class MsgInfo { ... }; // class for holding information
  // used to create a message
  template
  class MsgSender {
  public:
  ... // ctors, dtor, etc.

  void sendClear(const MsgInfo& info)
  {
  std::string msg;
  create msg from info;

  Company c;
  c.sendCleartext(msg);
  }
  void sendSecret(const MsgInfo& info) // similar to sendClear, except
  { ... } // calls c.sendEncrypted
  };

  这个能够很好地工作,但是假设我们有时需要在每次发送消息的时候把一些信息记录到日志中。通过一个 derived class(派生类)可以很简单地增加这个功能,下面这个似乎是一个合理的方法:

  template
  class LoggingMsgSender: public MsgSender {
  public:
  ... // ctors, dtor, etc.
  void sendClearMsg(const MsgInfo& info)
  {
  write "before sending" info to the log;
  sendClear(info); // call base class function;
  // this code will not compile!
  write "after sending" info to the log;
  }
  ...
  };

  注重 derived class(派生类)中的 message-sending function(消息发送函数)的名字 (sendClearMsg) 与它的 base class(基类)中的那个(在那里,它被称为 sendClear)不同。这是一个好的设计,因为它避开了 hiding inherited names(隐藏继续来的名字)的问题(参见《C++箴言:避免覆盖通过继续得到的名字》)和重定义一个 inherited non-virtual function(继续来的非虚拟函数)的与生俱来的问题(参见《C++箴言:绝不重定义继续的非虚拟函数》)。但是上面的代码不能通过编译,至少在符合标准的编译器上不能。这样的编译器会抱怨 sendClear 不存在。我们可以看见 sendClear 就在 base class(基类)中,但编译器不会到那里去寻找它。我们有必要理解这是为什么。

  问题在于当编译器碰到 class template(类模板)LoggingMsgSender 的 definition(定义)时,它们不知道它从哪个 class(类)继续。当然,它是 MsgSender,但是 Company 是一个 template parameter(模板参数),这个直到更迟一些才能被确定(当 LoggingMsgSender 被实例化的时候)。不知道 Company 是什么,就没有办法知道 class(类)MsgSender 是什么样子的。非凡是,没有办法知道它是否有一个 sendClear function(函数)。

  为了使问题具体化,假设我们有一个要求加密通讯的 class(类)CompanyZ:


  class CompanyZ { // this class offers no
  public: // sendCleartext function
  ...
  void sendEncrypted(const std::string& msg);
  ...
  };

  一般的 MsgSender template(模板)不适用于 CompanyZ,因为那个模板提供一个 sendClear function(函数)对于 CompanyZ objects(对象)没有意义。为了纠正这个问题,我们可以创建一个 MsgSender 针对 CompanyZ 的特化版本:

  template<> // a total specialization of
  class MsgSender { // MsgSender; the same as the
  public: // general template, except
  ... // sendCleartext is omitted
  void sendSecret(const MsgInfo& info)
  { ... }
  };

  注重这个 class definition(类定义)开始处的 "template <>" 语法。它表示这既不是一个 template(模板),也不是一个 standalone class(独立类)。正确的说法是,它是一个用于 template argument(模板参数)为 CompanyZ 时的 MsgSender template(模板)的 specialized version(特化版本)。这以 total template specialization(完全模板特化)闻名:template(模板)MsgSender 针对类型 CompanyZ 被特化,而且这个 specialization(特化)是 total(完全)的——只要 type parameter(类型参数)被定义成了 CompanyZ,就没有剩下能被改变的其它 template's parameters(模板参数)。
  已知 MsgSender 针对 CompanyZ 被特化,再次考虑 derived class(派生类)LoggingMsgSender:

  template
  class LoggingMsgSender: public MsgSender {
  public:
  ...
  void sendClearMsg(const MsgInfo& info)

  {
  write "before sending" info to the log;
  sendClear(info); // if Company == CompanyZ,
  // this function doesn't exist!
  write "after sending" info to the log;
  }
  ...
  };

  就像注释中写的,当 base class(基类)是 MsgSender 时,这里的代码是无意义的,因为那个类没有提供 sendClear function(函数)。这就是为什么 C++ 拒绝这个调用:它认可 base class templates(基类模板)可以被特化,而这个特化不一定提供和 general template(通用模板)相同的 interface(接口)。结果,它通常会拒绝在 templatized base classes(模板化基类)中寻找 inherited names(继续来的名字)。在某种意义上,当我们从 Object-oriented C++ 跨越到 Template C++,inheritance(继续)会停止工作。

  为了重新启动它,我们必须以某种方式使 C++ 的 "don't look in templatized base classes"(不在模板基类中寻找)行为失效。有三种方法可以做到这一点。首先,你可以在调用 base class functions(基类函数)的前面加上 "this->":

  template
  class LoggingMsgSender: public MsgSender {
  public:
  ...

  void sendClearMsg(const MsgInfo& info)
  {
  write "before sending" info to the log;
  this->sendClear(info); // okay, assumes that
  // sendClear will be inherited
  write "after sending" info to the log;
  }
  ...
  };

  第二,你可以使用一个 using declaration,假如你已经读过《C++箴言:避免覆盖通过继续得到的名字》,这应该是你很熟悉的一种解决方案。该文解释了 using declarations 如何将被隐藏的 base class names(基类名字)引入到一个 derived class(派生类)领域中。因此我们可以这样写 sendClearMsg:

  template
  class LoggingMsgSender: public MsgSender {
  public:
  using MsgSender::sendClear; // tell compilers to assume
  ... // that sendClear is in the
  // base class
  void sendClearMsg(const MsgInfo& info)
  {
  ...
  sendClear(info); // okay, assumes that
  ... // sendClear will be inherited
  }
  ...
  };

  (虽然 using declaration 在这里和《C++箴言:避免覆盖通过继续得到的名字》中都可以工作,但要解决的问题是不同的。这里的情形不是 base class names(基类名字)被 derived class names(派生类名字)隐藏,而是假如我们不告诉它去做,编译器就不会搜索 base class 领域。)

  最后一个让你的代码通过编译的办法是显式指定被调用的函数是在 base class(基类)中的:

  template
  class LoggingMsgSender: public MsgSender {
  public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {
  ...
  MsgSender::sendClear(info); // okay, assumes that
  ... // sendClear will be
  } // inherited

  ...
  };

  通常这是一个解决这个问题的最不合人心的方法,因为假如被调用函数是 virtual(虚拟)的,显式限定会关闭 virtual binding(虚拟绑定)行为。

  从名字可见性的观点来看,这里每一个方法都做了同样的事情:它向编译器保证任何后继的 base class template(基类模板)的 specializations(特化)都将支持 general template(通用模板)提供的 interface(接口)。所有的编译器在解析一个像 LoggingMsgSender 这样的 derived class template(派生类模板)是,这样一种保证都是必要的,但是假如保证被证实不成立,真相将在后继的编译过程中暴露。
例如,假如后面的源代码中包含这些,

  LoggingMsgSender zMsgSender;
  MsgInfo msgData;
  ... // put info in msgData
  zMsgSender.sendClearMsg(msgData); // error! won't compile

  对 sendClearMsg 的调用将不能编译,因为在此刻,编译器知道 base class(基类)是 template specialization(模板特化)MsgSender,它们也知道那个 class(类)没有提供 sendClearMsg 试图调用的 sendClear function(函数)。

  从根本上说,问题就是编译器是早些(当 derived class template definitions(派生类模板定义)被解析的时候)诊断对 base class members(基类成员)的非法引用,还是晚些时候(当那些 templates(模板)被特定的 template arguments(模板参数)实例化的时候)再进行。C++ 的方针是宁愿早诊断,而这就是为什么当那些 classes(类)被从 templates(模板)实例化的时候,它假装不知道 base classes(基类)的内容。

  Things to Remember

  ·在 derived class templates(派生类模板)中,可以经由 "this->" 前缀引用 base class templates(基类模板)中的名字,经由 using declarations,或经由一个 eXPlicit base class qualification(显式基类限定)。
上一篇:C++箴言:理解typename两个含义 人气:286
下一篇:C++箴言:考虑可选的虚拟函数的替代方法 人气:333
浏览全部C/C++的内容 Dreamweaver插件下载 常用网页广告代码全集
  最新网站源码 最新软件下载
2008-7-24 Sablog-X v2.0 预览版
2008-7-24 帝国备份王EmpireBak 2008 正式版
2008-7-24 网趣网上购物系统时尚版 v8.2
2008-7-24 纵横B2B电子商务系统XYECS!B2B v
2008-7-24 e路小说小偷 v1.2.0723
2008-7-24 凌风美女图片站程序 v2.2
2008-7-24 TOM15电影收索程序
2008-7-24 清风信息自动采集生成系统 v1.0
2008-7-24 QQ邮箱编辑器 v1.0 (小小菜刀ASP
2008-7-19 UltraEdit 简体中文增强版 14.10
2008-7-19 CentOS 5.2 i386 LiveCD
2008-7-19 Snapture多功能相机 v1.4
2008-7-19 iAcces中文输入法 v1.0Build016
2008-7-19 Cookbook烹饪秘籍 v2.5
2008-7-19 苹果专用DVD转换工具 v1.1.59汉化
2008-7-19 Modem修复软件ZiPhone修改版04.0
2008-7-19 AgileMessenger即时通讯工具美化
2008-7-19 Sketches画图软件 v0.7b6破解版


  发表评论
姓 名: 验证码:
内 容:
[ 汉字翻译拼音 ] [ 广告代码 ] [ 符号对照表 ] [ 进制转换 ] [ 经典小工具 ] [ 个税计算 ] [ 汉字简繁转换 ] [ 普通单位换算 ] [ 公制单位换算 ]
[ 生辰老黄历 ] [ 国内电话区号 ] [ 国家代码与域名缩写 ] [ 文字加密解密 ] [ 健康查询 ] [ 万年历 ] [ 手机号码查询 ] [ ip搜索 ] [ Google PR查询 ]
业务联系 | 广告刊登 | 频道合作 | 投稿荐稿 | 联系方式 | 加入收藏 | RSS订阅
Copyright © 2000-2008 www.knowsky.com All rights reserved | 网络实名:动态网站制作指南 | 沪ICP备05001343号