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
标签:std,sz,dst,swap,CharStore,copy,store From: https://blog.51cto.com/u_13137973/8319482