2013-08-06 88 views
20

我一直在測試一些C++ 11的一些功能。 我遇到了r值引用和移動構造函數。C++ 11右值引用調用拷貝構造函數也

我實現了我的第一個移動構造函數,那就是:

#include <iostream> 
#include <vector> 
using namespace std; 

class TestClass{ 

public: 
    TestClass(int s): 
     size(s), arr(new int[s]){ 
    } 
    ~TestClass(){ 
     if (arr) 
      delete arr; 
    } 
    // copy constructor 
    TestClass(const TestClass& other): 
      size(other.size), arr(new int[other.size]){ 
     std::copy(other.arr, other.arr + other.size, arr); 
    } 

    // move constructor 
    TestClass(TestClass&& other){ 
     arr=other.arr; 
     size=other.size; 

     other.arr=nullptr; 
     other.size=0; 
    } 

private: 
    int size; 
    int * arr; 
}; 

int main(){ 
    vector<TestClass> vec; 

    clock_t start=clock(); 
    for(int i=0;i<500000;i++){ 
     vec.push_back(TestClass(1000)); 
    } 
    clock_t stop=clock(); 
    cout<<stop-start<<endl; 

    return 0; 
} 

的代碼工作正常。無論如何把一個std :: cout裏面的複製構造函數,我注意到它被調用!並且很多次(移動構造函數500000次,複製構造函數524287次)。

更讓我吃驚的是,如果我從代碼註釋掉複製構造函數,整個程序會快得多,這次移動構造函數被稱爲1024287次。

任何線索?

+1

您正在使用哪種編譯器? – doctorlove

+0

http://coliru.stacked-crooked.com/view?id=0b61fede4fd9aef84b124760919e8ca8-4c5ca02fa47b506419b4501a6b65fb4f –

+0

我正在使用gcc 4.8.1 –

回答

30

noexcept你的移動構造函數:

TestClass(TestClass&& other) noexcept { 

闡述:我要給這一個皮埃爾,但不幸的是,cppreference源僅僅是大致正確。

在C++中03

vector<T>::push_back(T) 

具有 「強異常保證」。這意味着如果push_back引發異常,矢量將保持其在調用push_back之前的相同狀態。

如果移動構造函數拋出異常,則此擔保有問題。

vector重新分配,它會像移動舊緩衝區中的元素到新的。但是,如果這些移動中的任何一個引發異常(除第一個之外),則它將保留舊緩衝區已被修改的狀態,並且新緩衝區尚未包含它應該包含的所有內容。 vector無法將舊緩衝區恢復到其原始狀態,因爲它必須將元素移回原來的位置,那些移動也可能失敗。

所以規則是爲C++ 11條規定:

  1. 如果Tnoexcept移動構造函數,可以使用的元素從舊的緩衝區的新舉措。

  2. 否則,如果T有一個拷貝構造函數,那就用它來代替。否則(如果沒有可訪問的拷貝構造函數),那麼移動構造函數將被使用畢竟,但是在這種情況下,強大的異常安全保證不再被給出。

澄清:「拷貝構造函數」第2條規則是指一個構造採取const T&,那些威尼所謂T&拷貝構造函數的一個也沒有。在您的移動構造:-)

+1

你能詳細解釋一下你的答案嗎?不接受什麼? – Alon

+0

在Visual Studio上完全解答:-(除了說「錯誤C3646:'noexcept':未知覆蓋說明符」 – doctorlove

+0

@Alon http://accu.org/index。php /會議/ accu_conference_2013/accu2013_sessions#move_noexcept_and_push_back_and_how_it_relates_to_each_other – doctorlove

14

使用noexcept

TestClass(TestClass&& other) noexcept { ... } 

noexcept沒有常量表達式像這相當於noexcept(true)

編譯器可以使用該信息來對非拋功能使某些優化以及使noexcept運算符,它可以在編譯時檢查是否一個特定的表達式被聲明爲引發任何異常。

例如,如果元素的move構造函數爲noexcept,則容器(如std :: vector)將移動它們的元素,否則將複製它們。

來源:http://en.cppreference.com/w/cpp/language/noexcept_spec

NB:這是一個C++ 11特徵。某些編譯器可能還沒有實現它...(例如:Visual Studio 2012

-1

另一個問題。在移動的構造,

// move constructor 
TestClass(TestClass&& other){ 
    arr=other.arr; 
    size=other.size; 

    other.arr=nullptr; 
    other.size=0; 
} 

不是應該

ARR = STD:移動(other.arr);

size = std:move(other.size);

因爲

事實上,所有的命名值(如函數參數)始終評估爲左值(甚至是那些聲明爲右值引用)

0

使用std::vector內部的全部保留內存時將調用複製構造函數。在添加元素之前,需要調用std::vector::reserve()方法。

vector<TestClass> vec; 
vec.reserve(500000);