《Effective C++》第四章:设计与声明

26 篇文章 1 订阅

条款20:宁以pass-by-reference-to-const替换pass-by-value

这个条款其实也可以用pass-by-pointer-to-const来替代。因为如果传值,对于用户自定义类型来说十分费时,而使用const类型的引用或者指针来传递,不仅省时,而且保护了原对象不会被修改。但是对于内置类型、STL的迭代器(不是很懂)来说,传递value往往更合适。

条款21:必须返回对象时,别妄想返回其reference

原因很明确,函数返回时,一般返回的是局部变量,local对象在函数退出前就被销毁了,如果返回引用,则会出现错误。你可能会提出在函数内建立一个heap上的对象,这样就不会被销毁。但是这同样得付出一个构造函数的代价,同时谁该对这个heap对象调用delete?很容易出现内存泄漏。因此还是老老实实返回value,编译器会为其优化,即NRV。

条款25:考虑写出一个不抛出异常的swap函数

我们考虑一下标准库提供的swap函数:

namespace std{
template<typename T>
void swap(T &a, T &b)
{
      T temp(a);
      a = b;
      b= a;
}
}

这个版本 swap实现比较平淡,效率比较低下。对于一些类型而言,这样复制完全没必要,比如“以指针指向一个对象,内含真正数据”的类型:

class WidgetImpl
{
public:

private:
    int a,b,c;
};

class Widget
{
public:
    Widget(const Widget &rhs);
    Widget& operator=(const Widget& rhs)
{
    ...
    *pImpl = *(rhs.pImpl);
    ...
private:
    WidgetImpl *pImpl;
}
};
一旦要swap两个Widget对象,我们所需要做的只是交换它们的pImpl指针。但是STL提供的缺省版本效率让人...或许我们可以为这个函数针对Widget特化:
namespace std
{
template<>
void swap<Widget>(Widget& a, Widget &b)
{
    swap(a.pImpl,b.pImpl);
}
}
很遗憾,不能通过编译。因为pImpl是私有成员,不能直接访问,或许我们可以为Widget定义一个成员函数:

void Widget::swap(Widget &other)
{
    using std::swap;
    swap(pImpl, other.pImpl);
}
//然后在特化版本里调用成员函数
namespace std
{
template<>
void swap<Widget>(Widget &a, Widget &b)
{
    a.swap(b);
}
}
看起来十分美好,不仅能够通过编译,还与STL具有一致性。然而假设Widget和WidgetImpl都是类模板:

template<typename T>
class WidgetImpl{};

template<typename T>
class Widget{};
在Widget内放个swap成员函数,我们却在特化std::swap时候的出现错误:

namespace std
{
template<typename T>
void swap<Widget<T> >(Widget<T>& a,Widget<T> &b)
{
    a.swap(b);
}
}
问题在于C++不支持对函数模板进行偏特化,只支持全特化。因为对函数模板进行偏特化通常可以简单为其增加一个重载来解决这个问题。但是由于swap属于std特殊命名空间,我们可以全特化std内的templates,但是不能添加新的templates。那该怎么办?我们可以自己定义一个命名空间,如下:

namespace WidgetStuff
{
    .....
template<typename T>
void swap(Widget<T> &a, Widget<T> &b)
{
    a.swap(b);
}
}
目前我们所写的每一样东西都和swap编写者有关系。如果从客户观点看,假如你正在写一个function template,需要交换两个对象的值,你并不知道要调用哪个版本的swap,或者你并不知道有哪些版本的swap可供调用,或许下面的做法能够达到比较好的效果:

template<typename T>
void func(T &obj1, T &obj2)
{
    using std::swap;
    swap(obj1, obj2);
}
如果T是位于Widget并位于WidgetStuff内,编译器会使用此命名空间内的swap函数,如果没有专属的swap函数存在,则调用std::swap的函数,这得感谢using为其增加可见。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值