设为首页 加入收藏

TOP

条款3:大小写不敏感的字符串--之二(1)
2013-10-07 15:18:12 来源: 作者: 【 】 浏览:70
Tags:条款 大小写 敏感 字符串 之二

条款3:大小写不敏感的字符串--之二(1)

我们在条款2中创建了ci_string类,那么这个类的可用性到底如何?现在就来关注这个类的可用性,看看将会遇到怎样的设计问题或者需要做哪些的权衡。此外,我们还将解决其中一些遗留问题。

我们再来看看条款2中的解答(暂时忽略函数体部分):

  1. struct ci_char_traits : public char_traits<char>   
  2. {  
  3.   static bool eq( char c1, char c2 )   { /*...*/ }  
  4.   static bool lt( char c1, char c2 )   { /*...*/ }  
  5.   static int compare( const char* s1,  
  6.                           const char* s2,  
  7.                           size_t n )       { /*...*/ }  
  8.   static const char*  
  9.   find( const char* s, int n, char a ) { /*...*/ }  
  10. };  

在本条款中,我们要尽可能完整地回答下列相关问题:

1.将ci_char_traits从char_traits<char>继承下来,这种做法是否安全?

2.为什么在编译下面代码时会失败?

  1. ci_string s = "abc";   
  2. cout << s << endl

3.如果使用其他的运算符(例如, +, +=, =),并将string对象和ci_string对象作为参数,结果将会怎样?例如:
  1. string    a = "aaa";   
  2. ci_string b = "bbb";  
  3. string    c = a + b; 

解答

上述三个问题的解答如下:

1.将ci_char_traits从char_traits<char>继承下来,这种做法是否安全?

公有继承通常用来对"IS-A"或者"WORKS-LIKE-A"这样的关系进行建模,在Liskov替换规则(LSP)中描述了这种情况(请参见条款22和条款28)。不过,本例是LSP中一个非常特殊的情况:因为我们的主要意图并不是要通过基类char_traits<char>的指针或引用以多态的方式来使用ci_char_traits对象。标准库也不会通过多态的方式来使用trait对象。在这种情况下,使用继承只是为了实现上的便利性(当然,有些人可能会说这是在偷懒),因此在这里并不是遵循面向对象的方式来使用继承。

此外,在其他方面也用到了LSP:在编译期用到了LSP,编译期的派生类对象与基类对象必须是WORK-LIKE-A关系,这种行为是basic_string模板的需求定义所要求的。在新闻组上,Nathan Myers 是这样来描述的:

换句话说,LSP在这里仅应用于编译期,并且按照惯例我们将其称为"需求列表"。我希望将这个示例与其他示例区分开来,将这个示例中应用的规则定义为范型Liskov替换规则(Generic Liskov Substitution Principle,GLSP):任何作为模板参数的类型(或者模板),都应该与模板参数的需求列表保持一致。

从迭代器的tag或者trait中派生出来的类,都应该符合GLSP,而在传统LSP中的一些要素(例如,虚析构函数等),可以实现也可以不实现,这取决于在需求列表中是否定义了运行期的多态行为。

因此,简单来说,这里的继承是安全的,因为它符合GLSP(而不是LSP)。不过,我在这里使用继承并不仅仅只是为了便利性(避免编写char_traits<char>的其他功能代码),而且还为了说明其中的区别--只需修改4个函数就能获得想要的效果。

提出第一个问题的目的是要让你思考以下几点:(1)对继承的合理使用(以及不合理的使用);(2)在ci_char_traits中只包含静态成员,这种做法的意义;(3)永远都不以多态的方式来使用char_traits对象。

2.为什么在编译下面代码时会失败?

  1. ci_string s = "abc";   
  2. cout << s << endl;  

提示:在C++(www.cppentry.com)标准的21.3.7.9 [lib.string.io]中,对basic_string中 operator <<的声明如下所示。
  1. template<class charT, class traits, class Allocator>   
  2. basic_ostream<charT, traits>&  
  3. operator<<(basic_ostream<charT, traits>& os,  
  4.              const basic_string<charT,traits,Allocator>& str);  

解答:首先注意到cout实际上是一个basic_ostream<char, char_traits<

char> >类型的对象。接下来,我们就可以指出问题所在了:basic_string 中的operator <<是一个需要模板化的函数,并且在执行流插入操作时,参数basic_ostream中的"char type"和 "traits type" 必须与字符串类中的模板参数一样。也就是说,虽然在上面的解答中ci_char_traits是继承于char_traits<char>,但是正确的operator<<应该是将ci_string输出到一个basic_ostream<char, ci_char_traits>类型的流对象中去,而cout并不是这种类型的流对象。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇条款3:大小写不敏感的字符串--之.. 下一篇条款2:大小写不敏感的字符串--之..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: