get 函数调用取代直接取参数);trl::shared_ptr 支持定制性删除器,这能解决 cross-DLL 问题,即对象在一个动态链接库(DLL)中被 new 创建,又在另一个 DLL 中被 delete。【多使用 shared_ptr】if(a*b==c*d) 就会产生不符合预期的效果。a、b 的乘积,还是 c、d 的乘积,最后都是返回函数内同一个 static 变量的结果,所以判断只能为 true。get、set 函数来获取、设置该成员变量。
Rational 类中重载了乘法操作符,且其中一个参数是同类;且 Rational 构造函数不添加 explicit,即允许 int 转 Rational:Rational A;
//如果Rational的构造函数是explicit的,则下面两个式子都会错误
result = A * 2;// 是允许的,因为2是int,会转成Rational
result = 2 * A;// 是错误的,因为2是没有相应的class,没有operator*成员函数
A*2)的时候,int 在 Rational 构造函数的参数类型内,所以成功转换。Rational 能实现混合运算(即 A*2=2*A),则需要将成员函数中的 operator* 改成非成员函数。const Rational operator*(Rational& A, Rational& B) 即可。
swap 函数原本只是 STL 的一部分,而后成为异常安全性编程(exception-safe programming)的脊柱,以及用来处理自我赋值可能性的一个常见机制。
通过代码进行分析 swap 函数实现的不同:
// 一、最基础的swap函数(默认)
namespace std {
template<typename T> // std::swap的典型实现
void swap( T& a, T& b ) // 置换a和b
{
// 只要类型T支持copying,swap实现函数就会交换swap
// 但是进行了很多复制,整个swap太慢了
T temp(a);
a = b;
b = temp;
}
}
// 二、pimpl手法(pointer to implementation的缩写)
// 以指针指向一个对象,内含真正数据。
class WidgetImpl { // 针对Widget数据而设计的class
public:
...
private:
int a,b,c;
std::vector<double> v; // 真正的数据有vector数组,意味着复制时间更长
...
};
class Widget { // 这个class使用pimpl手法
public:
Widget( const Widget& rhs );
Widget& operator=( const Widget& rhs ) // 复制Widget时,令它复制其WidgetImpl对象
{
...
*pImpl = *(rhs.pImpl);
...
}
...
private:
WidgetImpl* pImpl;// 注意是private的
};
// 当Widgets被置换时,真正该做的是置换其内部的pImpl指针。
// 所以需要将std::swap对Widgets对象进行特化:
namespace std {
template<>
void swap<Widget>( Widget& a, Widget& b )
{
swap(a.pImpl,b.pImpl); // 只要置换它们指针即可
}
}
// 但目前这个全特化版本是无法通过编译的,因为它试图调用private的成员函数
// 所以有两个解决方法:
// 可以将这个特化版本声明为friend;
// 可以令Widget声明为一个名为swap的public成员函数做真正的置换工作,然后将std::swap特化,令它调用该成员函数:
class Widget { // 与之前相同,唯一差别是增加swap函数
public:
...
void swap( Widget& other )
{
using std::swap; // 这个声明很必要
swap(pImpl,other.pImpl); // 若要置换Widgets就置换其pImpl指针
}
...
};
namespace std {
template<> // 修订后的std::swap特化版本
void swap<Widget>( Widget& a,Widget& b )
{
a.swap(b);
}
}
// 三、还有一种情况:假设Widget和WidgetImpl 都是class templates 而非 classes
// 我们可以试试将WidgetImpl 内的数据类型加以参数化:
template<typename T>
class WidgetImpl { ... };
template<typename T>
class Widget { ... };
// 在Widget内(以及WidgetImpl内,如果需要的话)放个swap成员函数就像以往一样简单
// 但我们却在特化std::swap时遇上乱流,因为C++只允许对class template进行偏特化:
namespace std {
template<typename T>
void swap<Widget T>(Widget<T>& a,Widget<T>& b) // 可惜,不合法,是错误的
{ a.swap(b); }
}
// 因此我们只能考虑重载function template,但这又有新的问题,
// 客户可以全特化std内的templates,
// 但不可以添加新的templates(或classes或functions或其他任何东西)到std里头。
namespace std {
template<typename T>
void swap(Widget<T>& a,Widget<T>& b)
{ a.swap(b); }
}
// 四、在这个情况下,我们再次考虑non-member函数
// 假设Widget的所有相关机能都被置于命名空间WidgetStuff内:
namespace WidgetStuff {
...
template<typename T>
class Widget { ... };
...
template<typename T>
void swap( Widget<T>& a,Widget<T>& b ) // 这里并不属于std命名空间
{
a.swap(b);
}
}
// 补充说明:
// C++的名称查找法则(name lookup rules )确保将找到global作用域或T所在之命名空间内的任何T专属的swap。
// 如果T是Widget并位于命名空间WidgetStuff内,编译器会使用“实参取决之查找规则”找出WidgetStuff内的swap。
// 如果没有T专属之swap存在,编译器就会使用std内的swap,这需要感谢using声明式让std::swap在函数内曝光。
// 然而即便如此编译器还是比较喜欢std::swap的T专属特化版,而非一般化的那个template,
// 所以如果你已针对T将std::swap特化,特化版本会被编译器挑中。
本条款对 default swap、member swap、non-member swap、std::swap 特化版本以及对 swap 的调用进行了讨论。
swap 的缺省实现码对你的 class 或 class template 提供可接受的效率,你不需要额外做任何事。【任何尝试置换那种对象的人都会取得缺省版本,而那将有良好的运作】swap 缺省实现版的效率不足(那几乎总是意味你的 class 或 template 使用了某种 pimpl 手法),试着做下面这些事:
swap 成员函数,让它高效地置换你的类型的两个对象值。swap 成员函数。std::swap,并令它调用你的 swap 成员函数。swap,请确定包含一个 using 声明式,以便让 std::swap 在你的函数内曝光可见,然后不加任何 namespace 修饰符,赤裸裸的调用 swap。swap 决不可抛出异常。总结:
std::swap 对你的类型效率不高时,提供一个 swap 成员函数,并确定这个函数不抛出异常。swap,也该提供一个 non-member swap 用来调用前者;对于 classes(而非 templates),也请特化 std::swap。swap 时应针对 std::swap 使用 using 声明式,然后调用 swap 并且不带任何命名空间资格修饰。