0
点赞
收藏
分享

微信扫一扫

copy-and-swap

半秋L 2023-11-11 阅读 128

copy-and-swap 为C++中的惯用法,用于在拷贝赋值函数中保证对象完整性, 提供异常保证,以代码为例【1】:

class CharStore {
public:
    CharStore(std::size_t size = 0):
        sz(size),
        store(std::make_unique<char[]>(sz)) {}
    // Copy-constructor
    CharStore(const CharStore& other):
        sz(other.sz),
        store(std::make_unique<char[]>(sz)){
            std::copy(other.store.get(), other.store.get() + sz, store.get());
    }
    // Copy-assignment
    CharStore& operator=(const CharStore& other) {
        // 1. prevent self-assignment
        if (this != &other) {
            // 2. delete old data
            store.reset();
            // 3.0 prepare to assign new data
            sz = other.sz; 
            // 3.1 create storage for assignment
            store = std::make_unique<char[]>(sz);
            // 3.2 assign new data
            std::copy(other.store.get(), other.store.get() + sz, store.get()); 
        } 
        return *this;
    }

        
    void copyStr(char const *p, size_t len) {
        auto dst = store.get();
        for (size_t i = 0; i < len; ++i) {
            dst[i] = p[i];
        }

    }

    void print() {
        auto arr = store.get();
        for (size_t i = 0; i < sz; ++i)
            std::cout <<  arr[i];
        std::cout << std::endl;
    }

    ~CharStore() {}
private:
    std::size_t sz;
    std::unique_ptr<char[]> store;
};

int main() 
{
    CharStore src(14);
    CharStore dst(14);
    char const *a = "This is test";
    char const *b = "That is test";
    src.copyStr(a, 13);
    dst.copyStr(b, 13);
    src.print();
    dst.print();

    dst = src;
    dst.print();
}

当拷贝赋值进行深拷贝时,释放了目的对象dst中的store指针,此时如果3.1步骤中make_unique失败抛出异常,导致拷贝赋值失败,而dst中store已经不可用;

而copy-and-swap提供了一种解决办法,修改上述代码为:

class CharStore {
public:
    CharStore(std::size_t size = 0):
        sz(size),
        store(std::make_unique<char[]>(sz)) {}
    // Copy-constructor
    CharStore(const CharStore& other):
        sz(other.sz),
        store(std::make_unique<char[]>(sz)){
            std::copy(other.store.get(), other.store.get() + sz, store.get());
    }
    // Copy-assignment
    CharStore& operator=(const CharStore& other) {
        CharStore temp(other);
        temp.swap(*this);
        return *this;
    }
    // Non-throwing swap function
    void swap(CharStore& second) {
        using std::swap;
        swap(sz, second.sz);
        swap(store, second.store);
    }
    
    void copyStr(char const *p, size_t len) {
        auto dst = store.get();
        for (size_t i = 0; i < len; ++i) {
            dst[i] = p[i];
        }
    }

    void print() {
        auto arr = store.get();
        for (size_t i = 0; i < sz; ++i)
            std::cout <<  arr[i];
        std::cout << std::endl;
    }

    ~CharStore() {}
private:
    std::size_t sz;
    std::unique_ptr<char[]> store;
};

int main() 
{
    CharStore src(14);
    CharStore dst(14);
    char const *a = "This is test";
    char const *b = "That is test";
    src.copyStr(a, 13);
    dst.copyStr(b, 13);
    src.print();
    dst.print();

    dst = src;
    dst.print();
}

CharStore引入swap函数,在拷贝赋值函数中先引入了temp临时对象,再将temp对象与dst对象进行swap;

此时如果临时对象由于异常创建失败,则不会影响dst对象的有效性;

此处通过using std::swap引入标准库中的swap函数:

void swap(CharStore& second) {
        using std::swap;
        swap(sz, second.sz);
        swap(store, second.store);
    }

如果需要swap的对象实现了自己的 swap 版本,则可用调用自己实现的版本,这里应用ADL进行匹配,可参见【2】中介绍,引用【3】的示例代码:

namespace Ns
{
    class A
    {
        int id{};
 
        friend void swap(A& lhs, A& rhs)
        {
            std::cout << "swap(" << lhs << ", " << rhs << ")\n";
            std::swap(lhs.id, rhs.id);
        }
 
        friend std::ostream& operator<< (std::ostream& os, A const& a)
        {
            return os << "A::id=" << a.id;
        }
 
    public:
        A(int i) : id{i} {}
        A(A const&) = delete;
        A& operator = (A const&) = delete;
    };
}
 
int main()
{
    int a = 5, b = 3;
    std::cout << a << ' ' << b << '\n';
    std::swap(a, b);
    std::cout << a << ' ' << b << '\n';
 
    Ns::A p{6}, q{9};
    std::cout << p << ' ' << q << '\n';
    // std::swap(p, q);  // 错误,不满足类型要求
    swap(p, q);       // OK:通过ADL找到类内定义的swap函数
    std::cout << p << ' ' << q << '\n';
}

参考资料

【1】https://www.educative.io/answers/what-is-the-copy-and-swap-idiom-in-cpp

【2】C++ ADL & CPO介绍

【3】https://zh.cppreference.com/w/cpp/algorithm/swap

举报

相关推荐

0 条评论