以下代码来源于《深入实践Boost:Boost程序库开发的94个秘笈》一书
制作一个可移植的使用右值引用的类
C++11标准的最大功能之一是右值引用。此功能允许修改临时对象,从它们那里“偷”资源。在C++03中没有有值引用,但使用Boost.Move库,可以写一些可移植的使用右值引用的代码。
左值:一个函数或者对象实例。
失效值:生命期即将结束的对象。
广义左值:包括左值和失效值。
右值:包括失效值、临时对象、以及不关联对象的值。
纯右值:非失效值的那些右值。
左右值鉴别最简单的办法:左值可以用取地址操作符”&“获取地址,右值无法使用”&“。
例如:
int x = 0; //对象实例,有名,x是左值
int* p = &++x; //可以取地址,++x是左值
++x = 10; //前置++返回的是左值,可以赋值
p = &x++; //后置++操作返回一个临时对象,不能取地址或赋值,是右值,编译错误
使用方法步骤:
- 把宏BOOST_COPYABLE_AND_MOVABLE(classname)放置在类的private节中
- 保留一个正常的拷贝构造函数
- 编写一个复制赋值函数,参数为BOOST_COPY_ASSIGN_REF(classname)
- 编写一个移动构造函数和一个移动赋值函数,参数为BOOST_RV_REF(classname)
#include <iostream>
#include <string>
#include <boost/variant.hpp>
#include <boost/swap.hpp>
using namespace std;
namespace other {
class characteristics {};
}
struct person_info {
private:
// 这个宏声明person_info是一个可复制和可移植的类
// 必须要实现一个移动构造函数和拷贝赋值函数
BOOST_COPYABLE_AND_MOVABLE(person_info)
public:
bool is_male_;
std::string name_;
std::string second_name_;
other::characteristics characteristic_;
// person_info 的默认构造函数和swap调用是非常快 / 廉价
person_info() {}
person_info(const person_info& p)
:is_male_(p.is_male_)
,name_(p.name_)
,second_name_(p.second_name_)
,characteristic_(p.characteristic_)
{}
// Move constructot 移动构造函数
person_info(BOOST_RV_REF(person_info) person)
{
swap(person);
}
// Move assignment 移动赋值函数
person_info& operator=(BOOST_RV_REF(person_info) person)
{
if (this != &person)
{
swap(person);
person_info tmp;
tmp.swap(person);
}
return *this;
}
// Copy assignment 复制赋值函数
person_info& operator=(BOOST_COPY_ASSIGN_REF(person_info) person)
{
if (this != &person)
{
person_info tmp(person);
swap(tmp);
}
return *this;
}
void swap(person_info& rhs)
{
std::swap(is_male_, rhs.is_male_); //必须显示调用std域的swap,否则报错
name_.swap(rhs.name_);
second_name_.swap(rhs.second_name_);
boost::swap(characteristic_, rhs.characteristic_);
}
};
int main()
{
person_info vasya;
vasya.name_ = "Vasya";
vasya.second_name_ = "Snow";
vasya.is_male_ = true;
person_info new_vasya(boost::move(vasya));
assert(new_vasya.name_ == "Vasya");
assert(new_vasya.second_name_ == "Snow");
assert(vasya.name_.empty());
assert(vasya.second_name_.empty());
vasya = boost::move(new_vasya);
assert(vasya.name_ == "Vasya");
assert(vasya.second_name_ == "Snow");
assert(new_vasya.name_.empty());
assert(new_vasya.second_name_.empty());
return 0;
}
制作一个不可复制的类
为一个类提供一个复制构造函数和移动赋值运算符将需要太多的工作,或因技术原因一个类拥有一些绝对不可复制的资源:
例如,下面的例子:
class descriptor_owner
{
void* descriptor_;
public:
explicit descriptor_owner(const char* params)
{
descriptor_ = (void*)params;
}
~descriptor_owner()
{
system_api_free_descriptor(descriptor_);
}
};
int main()
{
descriptor_owner d1("O_o");
descriptor_owner d2("^_^");
// d2的描述符未正确释放
d2 = d1;
// d2的析构函数将释放描述符
// d1的析构函数将尝试释放已经释放的描述符
return 0;
}
boost::noncopyable可以避免这种情况,如果从它派生出你自己的类,C++编译器将不会产生复制构造函数和赋值运算符:
#include <boost/noncopyable.hpp>
class descriptor_owner_fixed : private boost::noncopyable
{
void* descriptor_;
public:
explicit descriptor_owner_fixed(const char* params)
{
descriptor_ = (void*)params;
}
~descriptor_owner_fixed()
{
system_api_free_descriptor(descriptor_);
}
};
int main()
{
descriptor_owner_fixed d1("O_o");
descriptor_owner_fixed d2("^_^");
// 将无法编译
d2 = d1;
// 也无法编译
descriptor_owner_fixed d3(d1);
return 0;
}
当然,我们可以使descriptor_owner_fixed的复制构造函数和赋值运算符私有,或者只定义它们而没有实现,也可以实现相同的结果。boost::noncopyable类目前正是这么实现。
制作一个不可复制但可移动的类
C++11提供了只可移动的(move-only)类(比如std::unique_ptr或std::thread)。采用这样的方法,可以做一个只可移动的descriptor_owner类:
class descriptor_owner1 : private boost::noncopyable
{
void* descriptor_;
public:
descriptor_owner1() : descriptor_(NULL) {}
explicit descriptor_owner1(const char* params) : descriptor_(_strdup(params)) {}
descriptor_owner1(descriptor_owner1&& param) : descriptor_(param.descriptor_)
{
param.descriptor_ = NULL;
}
descriptor_owner1& operator=(descriptor_owner1&& param)
{
clear();
std::swap(descriptor_, param.descriptor_);
return *this;
}
void clear()
{
free(descriptor_);
descriptor_ = NULL;
}
bool empty() const
{
return !descriptor_;
}
~descriptor_owner1()
{
clear();
cout << "~descriptor call" << endl;
}
};
descriptor_owner1 construct_descriptor2()
{
return descriptor_owner1("Construct using this string");
}
void foo_rv()
{
cout << "C++11" << endl;
descriptor_owner1 desc;
desc = construct_descriptor2();
assert(!desc.empty());
}
int main()
{
foo_rv();
system("pause");
return 0;
}
以上只能在C++11兼容编译器中工作。下面修改这个例子以便可用于C++03编译器。
使用方法步骤:
- 将BOOST_MOVEABLE_BUT_NOT_COPYABLE(classname)宏放置再private节。
- 编写一个移动构造函数和一个移动赋值函数,参数为BOOST_RV_REF(classname)。
#include <boost/move/move.hpp>
#include <boost/container/vector.hpp>
class descriptor_owner_movable
{
void* descriptor_;
BOOST_MOVABLE_BUT_NOT_COPYABLE(descriptor_owner_movable)
public:
descriptor_owner_movable() : descriptor_(NULL) {}
explicit descriptor_owner_movable(const char* param)
:descriptor_(_strdup(param)) {}
descriptor_owner_movable(BOOST_RV_REF(descriptor_owner_movable) param)
:descriptor_(param.descriptor_)
{
param.descriptor_ = NULL;
}
descriptor_owner_movable& operator=(BOOST_RV_REF(descriptor_owner_movable) param)
{
clear();
std::swap(descriptor_, param.descriptor_);
return *this;
}
void clear()
{
free(descriptor_);
descriptor_ = NULL;
}
};
descriptor_owner_movable construct_descriptor3()
{
return descriptor_owner_movable("Construct using this string");
}
int main()
{
descriptor_owner_movable movable;
movable = construct_descriptor3();
boost::container::vector<descriptor_owner_movable> vec;
vec.resize(10);
vec.push_back(construct_descriptor3());
vec.back() = boost::move(vec.front());
system("pause");
return 0;
}