Я тестировал некоторые функции C ++ 11 из некоторых.
Я наткнулся на ссылки r-значения и конструкторы перемещения.
Я реализовал свой первый конструктор перемещения, вот он:
#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 раз.
Любая подсказка?
Положил noexcept
на ходу конструктор:
TestClass(TestClass&& other) noexcept {
Разработка: я собирался дать этот Пьер, но, к сожалению, источник cppreference является только приблизительно правильным.
В С ++ 03
vector<T>::push_back(T)
имеет «гарантию сильного исключения». Это означает, что если push_back
выдает исключение, вектор остается в том же состоянии, в котором он находился до вызова push_back
,
Эта гарантия проблематична, если конструктор перемещения выдает исключение.
Когда vector
перераспределяет, было бы лайк в переехать элементы из старого буфера в новый. Однако, если любое из этих перемещений выдает исключение (кроме первого), то оно остается в состоянии, в котором старый буфер был изменен, и новый буфер еще не содержит всего, что он должен. vector
не может восстановить старый буфер в исходное состояние, потому что для этого придется переместить элементы обратно, те ходы также могут потерпеть неудачу.
Итак, для C ++ 11 было установлено правило:
Если T
имеет noexcept
Конструктор перемещения, который можно использовать для перемещения элементов из старого буфера в новый.
В противном случае, если T
имеет конструктор копирования, который будет использоваться вместо
В противном случае (если нет доступного конструктора копирования), в конце концов будет использован конструктор перемещения, однако в этом случае гарантия безопасности строгого исключения больше не предоставляется.
Пояснение: «конструктор копирования» в правиле 2 означает, что конструктор принимает const T&
не один из тех, кто так называемый weenie T&
Копировать конструкторы. 🙂
использование noexcept
на ходу конструктор:
TestClass(TestClass&& other) noexcept { ... }
noexcept
без постоянного выражения, как это эквивалентно noexcept(true)
,
Компилятор может использовать эту информацию, чтобы включить определенные оптимизации для функций, не генерирующих вызов, а также включить оператор noexcept, который может проверять во время компиляции, объявлено ли определенное выражение для каких-либо исключений.
Например, контейнеры, такие как std :: vector, будут перемещать свои элементы, если конструктор перемещения элементов noexcept, и копировать в противном случае.
Источник : http://en.cppreference.com/w/cpp/language/noexcept_spec
NB: Это C ++ 11 особенность. Определенный компилятор, возможно, еще не реализовал это … (например: Visual Studio 2012)
Конструктор копирования вызывается, когда вся зарезервированная память внутри std::vector
используется. Надо позвонить std::vector::reserve()
Метод перед добавлением элементов.
vector<TestClass> vec;
vec.reserve(500000);
Другой вопрос. В конструкторе перемещения
// move constructor
TestClass(TestClass&& other){
arr=other.arr;
size=other.size;
other.arr=nullptr;
other.size=0;
}
Не должно ли это быть
обр = станд: перемещение (other.arr);
Размер = станд: перемещение (other.size);
так как
тот факт, что все именованные значения (например, параметры функции) всегда оцениваются как lvalues (даже те, которые объявлены как ссылки на rvalue)
?