设为首页 加入收藏

TOP

STL扩展技术手册 编排体例
2013-10-07 00:32:27 来源: 作者: 【 】 浏览:75
Tags:STL 扩展 技术 手册 编排 体例

编排体例

我不管别人怎么看我。
-Peter Brock

峩的訚圉阑 不廓囵。(Me fail English That's unpossible.)
-Ralph Wiggum, The Simpsons

本书的编排体例大多不言而喻,所以我只略微涉及那些需要解释一下的地方。

字体

在正文中,以下各种成分的字体和大小写有所区别:API(例如glob API)、代码、概念(例如垫片概念)、<headername.hpp>、程序库(例如ACE)、字面量或路径(例如"my string"、NULL、123、/usr/include)、模式(例如Fa ade模式)、原则(例如最小意外原则)、垫片(例如垫片get_ptr)。代码清单则编排如下:

// In namespace namespace_name
class class_name
{
. . . // something that's a given, or has been shown already
public: // Class Section Name, e.g., "Construction"
class_name()
{
this->something_emphasized();
something_new_or_changed_from_previous_listing();                   
}
. . .
. . .和...

. . .表示之前见过的代码、例行代码,以及之前见过的或者暂不指定的模板参数列表。不要和...混淆了,后者表示省略号,用于变参函数签名式,以及捕捉所有异常的catch子句。
预先计算终点迭代器

为了保持代码片段易于理解,我采用手写的迭代器循环,而非预先计算迭代器终点。换句话说,本书的代码清单是这样的:

typedef unixstl::readdir_sequence   rds_t;
rds_t   files(".", rds_t::files);
for(rds_t::const_iterator b = files.begin(); b != files.end(); ++b)
{
std::cout << *b << std::endl;
}
而我通常的写法是:
. . .
for(rds_t::const_iterator b = files.begin(), e = files.end(); b != e;   
++b)                                                                    
{
std::cout << *b << std::endl;
}

后者在许多情况下效率更高(而且肯定在任何情况下都不会更差)。只对集合进行一次size()求值也有相同的效果。所以,尽管在本书别的地方你看不到这种用法,我还是给出以下建议:

提示

遍历迭代器区间时,尽量预先计算终点迭代器。使用索引时,尽量预先计算集合的大小。
当然,如果有可能我们应当使用算法,这是预先计算终点迭代器的最好方式,如下:

std::copy(files.begin(), files.end()
, std::ostream_iterator<rds_t::value_type>(std::cout, "\n"));
当上下文无歧义时,我会简化类型限定。例如,在定义方法Fibonacci_sequence::
const_iterator::operator ++()时,我没有完整地写出返回类型Fibonacci_
sequence::const_iterator::class_type&,而是像下面这样:
class_type& Fibonacci_sequence::const_iterator::operator ++()
{
. . .

要是你希望直接编译书中的代码,那就需要将这些权宜之计牢记在心。不过还好,所有的测试文件都包含在随书CD中,所以你就不必再去转抄了。

NULL

我将NULL用作指针,有两个理由。首先,不论你可能听到过什么样的相反意见,C++(www.cppentry.com)中确有强类型的NULL可用(Imperfect C++(www.cppentry.com)的15.1节有述)。其次,如果仅仅因为对编译器而言NULL和0是一个意思,就去赶时髦并将0用作指针,我觉得简直愚不可及。毕竟,代码首先是给人看的,然后才是编译器。

模板参数名

模板参数是一个或者两个大写字母,例如T(type/value-type)、S(sequence/string)、C(container/character)、VP(value policy type)等等。有些参数显示为完整的名字,例如CHOOSE_FIRST_TYPE,但在代码中还是一个或两个字母。这样做有两个原因。首先,在第三方程序库和应用程序中,有许多全大写字母的单词被用作宏定义符号。你一旦经历过模板参数和宏定义的命名冲突,就绝对不会想再来第二次,我敢保证!相反,我们可以假定宏定义不会使用一个(或者两个)字符。要是你发现自己使用的库违背这个假定,凭良心说,扔了它吧。
第二个理由是让成员类型和模板参数同名是无效的。换句话说,以下代码无法通过编译。

template <typename iterator>
struct thing
{
typedef iterator    iterator; // Compile error
};
因此,我完全相信,以下代码:
template <typename I>
struct thing
{
typedef I           iterator;
};
比这段代码清楚得多:
template <typename Iterator>
struct thing
{
typedef Iterator    iterator;
};

在定义模板类时,我几乎总是遵循一个惯例,即一开始就以上面短小的模板参数名字为基础,定义各个成员类型,并且在后续代码中不再使用前者。这样做很重要,因为定义中遗漏的成员类型将一目了然,而很多时候,这种缺失可能要过很长(因此极其烦人)时间才能被编译器发现。通过以单个字母定义类型参数,类模板必须立即定义所有这些成员类型,这是件好事。你大可不相信我。但我向你保证,这是我经历过最具生存力的方案。

成员类型的命名,以及名字空间中类型的命名

成员类型的命名方式通常为xyz_type,但既有或者标准的类型,例如iterator、pointer,等等除外;在局部作用域或者名字空间中,类型的命名方式为xyz_t。

调用约定

当提到的时候,分别用cdecl(和UNIX的调用规范等价)、fastcall和stdcall表示Windows的三种调用规范。除非另行指定,所有的COM方法和所有的Windows API函数都是stdcall,而其它所有都是cdecl,不过只要调用规范和讨论相关,我都会提出来。当指示调用规范时,我使用微软编译器的扩展关键字_cdecl、_fastcall、和_stdcall;其它编译器的关键字可能有所不同。
终点迭代器

C++(www.cppentry.com)标准用"过终点的值(past-the-end values)"称呼end()和rend(),及其等价形式所返回的迭代器。但为了确保清晰一致,我使用术语"终点迭代器"(endpoint iterators)。毕竟这些函数的名字不是past_the_end()和rpast_the_end()。当讨论某个迭代器迭代结束,我的提法是"到达终点",也就是说在这种状态下,迭代器和集合的end()方法返回的迭代器的比较关系是相等。

标准C名字的名字空间

我没有将C标准库的类型、函数名,以及其它保留名字放到std名字空间。这能给示例代码节省些篇幅,也和我在实践中的做法吻合。既然size_t或者strlen会在全局名字空间中一直存在,那么用std::size_t和std::strlen取而代之既徒劳无益,又分散精力。(以上是对我而言;其他作者或许有不同的看法。)

类适配器和实例适配器

某些论述模式的文献使用术语"适配器"(Adaptor)和"对象适配器"(Object Adaptor),分别指代在编译时适配一个类型,以及在运行时适配一个类型的实例。但这两个术语我都不喜欢;前者太流于一般,后者又用到了"对象",在软件开发文献中,这实在是一个已经泛滥成灾的词汇。我更喜欢"类适配器"和"实例适配器",这是我在本书中通篇使用的术语。

头文件名字

头文件总是用尖括号包围起来。这是为了在提及(愚蠢的)无后缀标准头文件时避免歧义。换句话说,在指头文件时,我使用<algorithm>,而不是模棱两可的algorithm 。

【责任编辑:董书 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇STL扩展技术手册 前言 下一篇STL扩展技术手册 序言

评论

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