zoukankan      html  css  js  c++  java
  • swap function & copy-and-swap idiom

    在C++中,众所周知在一个资源管理类(例如含有指向堆内存的指针)中需要重新定义拷贝构造函数、赋值运算符以及析构函数(Big Three),在新标准下还可能需要定义移动构造函数和移动赋值运算符(Big Five)。但实际上,这条规则还可以有一个小扩展。就是在资源管理类中,往往需要重新定义自己的swap函数来作为优化手段

    1. swap函数

    首先考察如下例子,假设类HasPtr中含有一个指向string的指针 *ps 和一个int类型值value。

    class HasPtr {
        public:
            ...
            ...
            ...
        private:
            string* ps;
            int value;
    };

    则如果没有定义自己的swap函数的话,调用标准库的std::swap相当于进行如下动作:

    HasPtr temp = v1;
    v1 = v2;
    v2 = temp; 

    这里v1中的string拷贝了两次,造成了不必要的效率上的浪费

    其实我们知道,这里swap只需交换指针指向就可以,所以我们可以写出如下自定义版本的swap函数:

    class HasPtr {
        friend void swap(HasPtr& lhs, HasPtr& rhs);
        ...
        ...
    };
    
    inline
    void swap(HasPtr& lhs, HasPtr& rhs) {
        using std::swap;
        swap(lhs.ps, rhs,ps);
        swap(lhs.value, rhs.value);
    }

    注意: 这里有一点值得注意的是,函数调用的时候应该写成swap,而非std::swap形式。这是因为假如有另一个类型Foo含有成员h(HasPtr类型),则如果HasPtr定义了自己的swap函数的话,应该调用其自定义的swap函数,否则调用标准库的std::swap。

    所以如下形式1是不对的,应该写成形式2所示。

    二者执行结果相同,但效率不同(前者不必要的拷贝)

    //形式1
    void swap(Foo& lhs, Foo& rhs) {
        std::swap(lhs.h, rhs.h);
    }
    //形式2
    void swap(Foo & lhs, Foo & rhs) {
        using std::swap;
        swap(lhs.h, rhs.h);
    }

    2. copy and swap idiom

    定义了swap函数的类常用swap来定义他们的赋值运算符,即copy and swap技术。

    这样做可以做到天然的异常安全并且正确处理自我赋值问题,是简洁高效安全的写法。(为什么其他方法存在异常安全或自我赋值问题可以参加effective c++条款11)

    上述例子使用copy and swap技术定义赋值运算符如下:

    HasPtr& HasPtr::operator=(HasPtr rhs) {
        swap(*this, rhs);
        return *this;
    }

    可以看出,不同于一般的赋值运算符,其参数采用传值方式,而非引用。它与如下代码功能上等价,但效率上更高(将copy动作移至函数参数构造阶段)

    HasPtr& HasPtr::operator=(const HasPtr& rhs) {
        HasPtr temp(rhs);
        swap(*this, temp);
        return *this;
    }

    这两种方式均可以完美解决异常安全和自我赋值问题。第一种方法被C++ primer和C++编程规范所推荐,Scott Meyers在effective c++中表达了一些对其可读性的忧虑,但如果掌握好copy and swap idiom,还是应该推荐采用第一种写法生成更高效的代码。

    参考资料:

    1.Stanley B. Lippman / Josée Lajoie / Barbara E. Moo,  C++ Primer 中文版(第 5 版)[M].  电子工业出版社,2013

    2.Meyers S. Effective C++[M]. Addison Wesley, 2005.

    3.Sutter H, Alexandrescu A. C++ Coding Standards: 101 Rules, Guidelines, and Best Practices (C++ in Depth Series)[M]. Addison-Wesley Professional, 2004.

    4.关于swap and copy idiom问题在stackoverflow上的解答: http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom

    //形式1

    void swap(Foo& lhs, Foo& rhs) {

        std::swap(lhs.h, rhs.h);

    }

    //形式2

    void swap(Foo & lhs, Foo & rhs) {

        using std::swap;

        swap(lhs.h, rhs.h);

    }

  • 相关阅读:
    隐马尔科夫模型
    cmake modules default path
    opencv mat
    cmake 查找头文件和库文件顺序
    opencv3.0 legacy和nonfree丢失
    qt include_directories没用
    ros中删除某个包之后用apt安装的包找不到
    sql-select
    关于ros stage与navigation仿真总结5月16号
    关系型数据库
  • 原文地址:https://www.cnblogs.com/wangxiaobao/p/5994921.html
Copyright © 2011-2022 走看看