使用C++11移動複製

以下代碼來源於《深入實踐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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章