分享
 
 
 

翻译:Effective C++, 3rd Edition, Item 25: 考虑支持不抛异常的 swap(上)

王朝c/c++·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

Item 25: 考虑支持不抛异常的 swap

swap 是一个有趣的函数。最早作为 STL 的一部分被引入,后来它成为异常安全编程(exception-safe programming)的支柱(参见 Item 29)和压制自赋值可能性的通用机制(参见 Item 11)。因为 swap 太有用了,所以正确地实现它非常重要,但是伴随它的不同寻常的重要性而来的,是一系列不同寻常的复杂性。在本 Item 中,我们就来研究一下这些复杂性究竟是什么样的以及如何对付它们。

交换两个对象的值就是互相把自己的值送给对方。缺省情况下,通过标准的交换算法来实现交换是非常成熟的技术。典型的实现完全符合你的预期:

namespace std {

template<typename T> // typical implementation of std::swap;

void swap(T& a, T& b) // swaps a's and b's values

{

T temp(a);

a = b;

b = temp;

}

}

只要你的类型支持拷贝(通过拷贝构造函数和拷贝赋值运算符),缺省的 swap 实现就能交换你的类型的对象,而不需要你做任何特别的支持工作

可是,缺省的 swap 实现可能不那么酷。它涉及三个对象的拷贝:从 a 到 temp,从 b 到 a,以及从 temp 到 b。对一些类型来说,这些副本全是不必要的。对于这样的类型,缺省的 swap 就好像让你坐着快车驶入小巷。

这样的类型中最重要的就是那些主要由一个指针组成的类型,那个指针指向包含真正数据的另一种类型。这种设计方法的一种常见的表现形式是 "pimpl idiom"("pointer to implementation" ——参见 Item 31)。一个使用了这种设计的 Widget 类可能就像这样:

class WidgetImpl { // class for Widget data;

public: // details are unimportant

...

private:

int a, b, c; // possibly lots of data -

std::vector<double> v; // expensive to copy!

...

};

class Widget { // class using the pimpl idiom

public:

Widget(const Widget& rhs);

Widget& operator=(const Widget& rhs) // to copy a Widget, copy its

{ // WidgetImpl object. For

... // details on implementing

*pImpl = *(rhs.pImpl); // operator= in general,

... // see Items 10, 11, and 12.

}

...

private:

WidgetImpl *pImpl; // ptr to object with this

}; // Widget's data

为了交换这两个 Widget 对象的值,我们实际要做的就是交换它们的 pImpl 指针,但是缺省的交换算法没有办法知道这些。它不仅要拷贝三个 Widgets,而且还有三个 WidgetImpl 对象,效率太低了。一点都不酷。

当交换 Widgets 的是时候,我们应该告诉 std::swap 我们打算做什么,执行交换的方法就是交换它们内部的 pImpl 指针。这种方法的正规说法是:针对 Widget 特化 std::swap(specialize std::swap for Widget)。下面是一个基本的想法,虽然在这种形式下它还不能通过编译:

namespace std {

template<> // this is a specialized version

void swap<Widget>(Widget& a, // of std::swap for when T is

Widget& b) // Widget; this won't compile

{

swap(a.pImpl, b.pImpl); // to swap Widgets, just swap

} // their pImpl pointers

}

这个函数开头的 "template<>" 表明这是一个针对 std::swap 的完全模板特化(total template specialization)(某些书中称为“full template specialization”或“complete template specialization”——译者注),函数名后面的 "<Widget>" 表明特化是在 T 为 Widget 类型时发生的。换句话说,当通用的 swap 模板用于 Widgets 时,就应该使用这个实现。通常,我们不允许改变 std namespace 中的内容,但允许为我们自己创建的类型(就像 Widget)完全特化标准模板(就像 swap)。这就是我们现在在这里做的事情。

可是,就像我说的,这个函数还不能编译。那是因为它试图访问 a 和 b 内部的 pImpl 指针,而它们是 private 的。我们可以将我们的特化声明为友元,但是惯例是不同的:让 Widget 声明一个名为 swap 的 public 成员函数去做实际的交换,然后特化 std::swap 去调用那个成员函数:

class Widget { // same as above, except for the

public: // addition of the swap mem func

...

void swap(Widget& other)

{

using std::swap; // the need for this declaration

// is explained later in this Item

swap(pImpl, other.pImpl); // to swap Widgets, swap their

} // pImpl pointers

...

};

namespace std {

template<> // revised specialization of

void swap<Widget>(Widget& a, // std::swap

Widget& b)

{

a.swap(b); // to swap Widgets, call their

} // swap member function

}

这个不仅能够编译,而且和 STL 容器保持一致,所有 STL 容器都既提供了 public swap 成员函数,又提供了 std::swap 的特化来调用这些成员函数。

可是,假设 Widget 和 WidgetImpl 是类模板,而不是类,或许因此我们可以参数化存储在 WidgetImpl 中的数据类型:

template<typename T>

class WidgetImpl { ... };

template<typename T>

class Widget { ... };

在 Widget 中加入一个 swap 成员函数(如果我们需要,在 WidgetImpl 中也加一个)就像以前一样容易,但我们特化 std::swap 时会遇到麻烦。这就是我们要写的代码:

namespace std {

template<typename T>

void swap<Widget<T> >(Widget<T>& a, // error! illegal code!

Widget<T>& b)

{ a.swap(b); }

}

者看上去非常合理,但它是非法的。我们试图部分特化(partially specialize)一个函数模板(std::swap),但是尽管 C++ 允许类模板的部分特化(partial specialization),但不允许函数模板这样做。这样的代码不能编译(尽管一些编译器错误地接受了它)。

当我们想要“部分特化”一个函数模板时,通常做法是简单地增加一个重载。看起来就像这样:

namespace std {

template<typename T> // an overloading of std::swap

void swap(Widget<T>& a, // (note the lack of "<...>" after

Widget<T>& b) // "swap"), but see below for

{ a.swap(b); } // why this isn't valid code

}

通常,重载函数模板确实很不错,但是 std 是一个特殊的 namespace,规则对它也有特殊的待遇。它认可完全特化 std 中的模板,但它不认可在 std 中增加新的模板(也包括类,函数,以及其它任何东西)。std 的内容由 C++ 标准化委员会单独决定,并禁止我们对他们做出的决定进行增加。而且,禁止的方式使你无计可施。打破这条禁令的程序差不多的确可以编译和运行,但它们的行为是未定义的。如果你希望你的软件有可预期的行为,你就不应该向 std 中加入新的东西。。

(未完待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有