您的位置:首页 > 编程语言 > C语言/C++

Effective C++ 第二版 27)函数隐式生成 28)名字空间

2013-10-22 11:15 856 查看
条款27 如果不想使用隐式生成的函数就要显式地禁止

假设想写一个类模板Array, 它生成的类除了可以进行上下限检查外, 其他行为和C++标准数组一样; 设计中面临的一个问题是怎么禁止Array对象之间的赋值操作;

Note 对标准C++数组来说赋值是非法的;

如果你不想使用某个函数, 只需简单地把它从类中移出;

Note 赋值运算符是比较特别的成员函数, 当你没有写这个函数时, C++会帮你写一个;

Solution: 声明这个函数(operator=), 声明为private; [无需定义]; 显式地声明一个成员函数, 可以防止编译器的自动生成版本, 声明为private, 防止其他人调用;

但是这个方法还不够安全, 成员函数和友元函数还是可以调用私有函数, 如果你不去定义(实现)这个函数的话, 当无意间调用了这个函数时, 程序在链接时就会报错;

e.g. 对于Array来说, 模板定义可以像这样:
>当用户试图对Array对象执行赋值操作时, 编译器就会报错, 当你无意间在成员或友元函数中调用它时, 链接器就会提醒;

这个例子适用于所有条款45中介绍的每一个编译器会自动生成的函数; 实际应用中, 赋值和拷贝构造函数具有行为上的相似性, 这意味着大多数时候你想禁止其中一个时, 也要禁止另一个;

条款28 划分全局名字空间

全局空间最大的问题是它本身只有一个; 在大型软件项目中, 经常有人把定义的名字都放在全局空间, 从而不可避免地导致名字冲突;

e.g. library1.h定义了一些常量: const double LIB_VERSION = 1.204; 类似地, library2.h也定义了: const int LIB_VERSION = 3;

当某个程序同时包含library1.h和library2.h的时候就会出问题; 对于这类问题, 只能自己编辑头文件来消除名字冲突; [还可以骂两句, 搞搞破坏, 发泄发泄之类的]

作为程序员, 你可以尽力使自己写的程序库不给别人带来这些问题; 例如, 可以预先想一些不大可能造成冲突的某种前缀, 加载每个全局符号之前, 这样组合的标识符会看起来比较奇怪;

比较好的方法是使用C++ namespace; namespace本质上和使用前缀的方法一样, 只是避免了要写前缀, 避免别人看到的是强行加上前缀的名字;

e.g. 前缀:
namespace:

用户可以通过三种方法访问名字空间里的符号:

1) 将名字空间的所有符号全部引入到某一用户空间; 2) 将部分符号引入到某一用户空间; 3) 通过修饰符显式地一次性使用某个符号;
Note 有些名字空间没有名字, 没有命名的名字空间一般用于限制名字空间内部元素的可见性, M31;

名字空间带来的好处之一: 潜在的二义性不会再造成错误(条款26); 从许多不同的名字空间引入名一个符号名不会造成冲突; (还没有使用这个符号的状态下)

e.g. 除了sdm外, 还要使用:
只要不引用符号Handle, 使用sdm和AcmeWindowSystem时就不会有冲突, 假如真的要引用, 可以明确地指明是哪个名字空间的Handle:
假如用常规的基于头文件的方法来做, 只是简单地包含sdm.h和acme.h, 由于Handle有多个定义, 编译无法通过;

C++标准库几乎所有的东西都存在于名字空间std中; 它以一种直接的方式影响到你: C++提供了看起来有趣的, 没有扩展名的头文件: <iostream>, <string>等(条款49);

在一些古老的编译器中, 可以用struct来近似实现namespace: 先创建一个结构来 保存全局符号名, 然后将这些全局符号名作为静态成员放入结构中:
[内部类]

现在, 如果有人想访问这些全局符号名, 只要简单地在他们前面加上结构名作为前缀:
如果全局范围内实际上没有名字冲突, 用户会觉得加修饰符麻烦而多余;

让用户选择使用它们或忽略的办法:

对于类型名, 可以用类型定义 typedef来显式地去掉空间引用: e.g. 假设结构s(模拟的名字空间)内有个类型名T, 可以使用typedef来使得T成为S::T的同义词: typedef sdm::Handle Handle;

对于结构中的每个(静态)对象X, 可以提供一个(全局)引用X, 初始化为S::X; const double& BOOK_VERSION = sdm::BOOK_VERSION; (条款47) 处理函数的方法和处理对象一样, 要注意即使定义函数的引用是合法的, 但代码的维护者会更喜欢使用函数指针:

Note getHandle是一个常量指针; 因为不允许用户将指针指向别的东西;

定义函数的引用:

除了初始化的方式外, 函数的引用和函数的常指针在行为上完全相同;

有了类型定义和引用, 不会遇到全局名字冲突的用户就会使用没有修饰符的类型和对象名; 那些有全局名字冲突的用户就忽略类型和引用的定义, 以待修饰符的符号名取代; 要注意不是所有用户都想使用简写名, 所以要把类型定义和引用放在单独的头文件中, 不要把它和(模拟namespace)结构的定义混在一起;

struct只是namespace的相似应用, 但实际上很多方面有欠缺; 明显的一点是对运算符的处理, 如果运算符被定义为结构 的静态成员, 他就只能通过函数调用来使用, 而不能像常规的运算符所设计的自然的中缀语法使用;
---类的设计和声明 End---
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐