C++11 rvalue ссылка вызов конструктора копирования тоже
Я тестировал некоторые функции C++11 из некоторых. Я наткнулся на ссылки r-value и конструкторы перемещения.
я реализовал свой первый конструктор перемещения, вот он:
#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 раз.
любой ключ?
4 ответов
поставить noexcept
на вашем конструкторе перемещения:
TestClass(TestClass&& other) noexcept {
уточнение: я собирался дать это Пьеру, но, к сожалению, источник cppreference только приблизительно правильный.
В C++03
vector<T>::push_back(T)
имеет "сильную гарантию исключения". Это означает, что если push_back
выбрасывает исключение, вектор остается в том же состоянии, что и до вызова push_back
.
эта гарантия проблематична если движение конструктор создает исключение.
когда vector
перераспределяет, это было как to движение элементы из старого буфера в новый. Однако, если какой-либо из этих ходов вызывает исключение (кроме первого), то он остается в состоянии, когда старый буфер был изменен, а новый буфер еще не содержит всего, что он должен. The vector
не удается восстановить старый буфер в исходное состояние, потому что он должен был бы двигаться элементы для этого те движется, возможно, тоже не удастся.
Итак, для C++11 было установлено правило:
если
T
естьnoexcept
переместить конструктор, который можно использовать для перемещения элементов из старого буфера в новый.в противном случае, если
T
имеет конструктор копирования, что будет использоваться вместо этого.в противном случае (если нет доступного конструктора копирования), то перемещение конструктор будет использоваться в конце концов, однако в этом случае сильная гарантия безопасности исключений больше не предоставляется.
уточнение: "конструктор копирования" в правиле 2 означает, что конструктор принимает const T&
, не один из тех сосисок так называемых 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::reserve()
метод перед добавлением элементов.
vector<TestClass> vec;
vec.reserve(500000);
еще один вопрос. В конструкторе move,
// move constructor
TestClass(TestClass&& other){
arr=other.arr;
size=other.size;
other.arr=nullptr;
other.size=0;
}
не должно быть
arr=std: move (other.arr);
size=std: move (другое.размер);
, потому что
тот факт, что все именованные значения (такие как параметры функции) всегда оцениваются как lvalues (даже те, которые объявлены как ссылки rvalue)
?