栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

一. 内容
  1. swap 是一个有趣的函数。原本它只是STL的一部分,后面成为异常安全性编程的重要支柱(见条款29),以及处理自我赋值的常见机制(见条款11)。swap如此有用,考虑其合理的实现变得十分重要。

  2. 所谓 swap(置换),就是将两个对象的值彼此赋值给对方。

  3. 默认情况下,swap 可以交给 标准程序库提供的 swap 算法完成,其典型实现正如预期。

    namespace std{
        template
        void swap(T& A,T& B) noexcept{
            T Temp(A);
            B=A;
            A=Temp;
        }
    }
    

    只要类型 T 支持 copying 函数,默认的 swap 函数就会为你置换对应的对象。

    但默认实现的 swap 十分平淡,它只是简单的复制了对象的值,而未对特殊情况做处理。举个例子,对于以指针指向一个对象,内含真正的数据的那种类型,也就是我们所说的 pimpl 手法(pointer to implemention),一旦要交换这种对象的值,默认的swap 会复制整个对象,但我们期望的是只需复制其中包含数据的指针。所以有必要编写自己的 swap 函数。

  4. 对于使用 pimpl 手法的对象,示例如下:

    #include 
    
    class WidgetData {
    public:
    };
    
    class Widget {
    public:
        Widget(WidgetData* mData): Data(mData) {}
    public:
        void swap(Widget& Other) {
            std::swap(Data, Other.Data);
        }
    
    private:
        WidgetData* Data;
    };
    namespace std {
        inline void swap(Widget& A, Widget& B) noexcept { //尝试全特化 std::swap失败,可以用重载代替。
            A.swap(B);
        }
    }
    

    我们可以在 Widget 类声明一个名为 swap 的 public 成员函数,然后同时在 std 命名空间提供特殊化的 swap 函数,令它调用该成员函数。

    这种做法不仅可以通过编译,还与 STL 容器保持一致性,因为所有 STL 容器都提供 public swap 成员函数和 std::swap 特化版本。

  5. 注意,C++只允许对 class templates 偏特化,禁止在 function templates 偏特化。一个比较好的替换方式是重载。但是注意 std 是一个特殊的命名空间,其管理规则也比较特殊,客户可以全特化 std 内的 templates,但不可以添加新的 classes,functions 或者其他任何东西到 std 中。标准委员会禁止我们膨胀那些已经声明好的东西。所以如果你希望你的软件有可预期的行为,请不要添加任何新东西到std里头。

  6. 目前为止,我编写的任何东西都与 swap 有关,这里已经存在多个版本的 swap,从客户的角度来看,我怎么确保可以调用到我想要的那个版本的 swap函数呢?其实,一旦编译器看到对 swap 的调用,它便会查找适当的 swap 并调用之。 C++ 的名称查找法则(name lookup rules)会先在当前作用域寻找目标函数,找不到再扩大作用域。而使用类似 using xxx,可以提醒编译器使用该xxx命名空间中函数。注意,特化版本的函数会优先考虑,当然,也要注意二义性的问题。

  7. 此刻,我们已经完成对所有情况的讨论,来做个总结。
    如果默认的 swap 实现有比较好的执行效率,那么就不需要做额外的事情。
    如果默认的 swap 实现效率不足,那意味着你的class或者template做了类似pimpl的事情,请试着做下面的事情:

    • 提供一个 public swap 成员函数,让它高效的置换你类型的两个对象值。稍后我将解释,这个函数绝不该抛出任何异常。
    • 在你的 classes 或 template 所在的命名空间内提供一个 non-member swap,并令它调用上述 swap 成员函数。
    • 如果你正编写一个 class,而非 class template,为你的 class 特化 std::swap。并令它调用你的 swap 成员函数。
  8. 唯一还未明确的是我的劝告:成员版 swap 绝不可抛出异常。因为 swap 一个最好的应用就是帮助 classes ,templats 提供强烈的异常安全报障。条款29对此提供了所有的细节。注意,该劝告只施行于成员版,不施行于非成员版。因为 swap 默认版本是以拷贝函数为基础,而两者都允许抛出异常。

二. 总结
  1. 当 std::swap 对你的类型效率不高时,提供一个 swap 成员函数,并确定这个函数不抛出异常。
  2. 如果你提供一个 member template,也该提供一个 non-member swap 用来调用前者。对于 classes,而非 templates,也请特化 std::swap。
  3. 调用 swap 时应针对 std::swap 使用 using 声明式,然后调用 swap 并且不带任何命名空间资格修饰。
  4. 为用户定义类型进行 std templates 全特化是好的,但千万不要尝试在 std 内加入某些对 std 而言是全新的东西。
暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1015133.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号