在我作为C ++程序员的时候,我通常认为应该避免名称隐藏,因为它可能导致混淆。例如,如果你有一个带有函数f()的基类和一个带有函数f()的派生类,并且f()不是虚拟的,那么调用者可能会意外地调用错误的f()。
然而,我在阅读Herb Sutter的Exceptional C ++书籍时遇到了一个例子,其中似乎鼓励了一个隐藏名字的案例。为了创建一个不区分大小写的字符串,他建议我们通过创建一个继承自std::basic_string<>
的{{1}}类来创建一个不区分大小写的char_traits
版本(这是一个非多态的静态类)像这样:
char_traits<char>
(我省略了函数定义以使其更简洁)。本质上,我们继承了struct ci_char_traits : public char_traits<char>
{
static bool eq( char c1, char c2 ) { /*...*/ }
static bool lt( char c1, char c2 ) { /*...*/ }
static int compare( const char* s1,
const char* s2,
size_t n) { /*...*/ }
static const char*
find( const char* s, int n, char a ) { /*...*/ }
}
的所有函数,我们隐藏了我们想要修改的函数,以使其不区分大小写。然后,您可以像这样定义不区分大小写的字符串:
char_traits<char>
现在我认为这是一个非常巧妙的技巧!但我想知道这种事情是否被认为是“良好实践”,因为隐藏名称通常是不受欢迎的。也许名称隐藏静态成员函数比其他类型的函数更容易接受?
答案 0 :(得分:1)
这里的区别在于traits类不是多态的,所以对于使用哪个接口永远不会有任何混淆。
ci_char_traits
当然可以通过私有继承,封装或简单地推迟到std::char_traits<>
来实现。所有这些方法都需要更多的维护。
评论中有关模板是否具有多态性的讨论。我会详细说明。
构造std::basic_string<char, ci_char_traits>
导致模板std::basic_string<class CharT, class Traits, class Allocator>
的模板扩展。在此扩展期间,进行以下类型替换:
CharT
已替换为char
Traits
已替换为ci_char_traits
Allocator
已替换为std::allocator<char>
因此,编译器生成的类是:
std::basic_string<char, ci_char_traits, std::allocator<char>>
这不是一个多态类。
这个类将有一个名为std::basic_string<char, ci_char_traits, std::allocator<char>>::traits_type
的成员typedef,它明确地是类型ci_char_traits
。
因此,此模板扩展将导致非多态类,该类使用非多态类ci_char_traits
来确定基于字符的操作(如比较)的行为。
类eq
中的代码将调用lt
和std::basic_string<char, ci_char_traits, std::allocator<char>>
的哪个重载被精确定义,这就是为什么在这种情况下,重载这些静态成员的做法并不错功能。
答案 1 :(得分:0)
在不区分大小写的示例中,更多的是实现接口,只是因为这个接口是通过模板完成的,所以它是编译时的事情,而不是运行时。
要实现std :: char_traits,你根本不能使用不同的名称(至少没有使用basic_string进行大量修改),这些名称是接口的一部分。