definicion - C++ 11 rvalue de referencia llamando al constructor de copia también
constructor copia java (4)
El constructor de copia se llama cuando se utiliza toda la memoria reservada dentro de std::vector
. Es necesario llamar al método std::vector::reserve()
antes de agregar los elementos.
vector<TestClass> vec;
vec.reserve(500000);
He estado probando algunas características de C ++ 11 de algunas algunas. Me encontré con referencias de valor r y mover constructores.
Implementé mi primer constructor de movimientos, aquí está:
#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;
}
El código funciona bien. De todos modos, colocando un std :: cout dentro del constructor de copias, ¡noté que se llama! Y muchas veces ... (mueva el constructor 500000 veces, copie el constructor 524287 veces).
Lo que más me sorprendió es que si comento el constructor de copia desde el código, todo el programa se vuelve mucho más rápido, y esta vez el constructor de movimiento se llama 1024287 veces.
¿Cualquier pista?
Otra pregunta. En el constructor de movimiento,
// move constructor
TestClass(TestClass&& other){
arr=other.arr;
size=other.size;
other.arr=nullptr;
other.size=0;
}
No deberia ser
arr = std: move (other.arr);
size = std: move (other.size);
porque
el hecho de que todos los valores nombrados (como los parámetros de la función) siempre se evalúan como valores de l (incluso aquellos declarados como referencias de valor de r)
?
Ponga noexcept
en su constructor de movimiento:
TestClass(TestClass&& other) noexcept {
Elaboración: iba a darle a éste Pierre, pero desafortunadamente la fuente de referencia de cpp es aproximadamente correcta.
En C ++ 03
vector<T>::push_back(T)
tiene la "fuerte garantía de excepción". Eso significa que si push_back
lanza una excepción, el vector se deja en el mismo estado que tenía antes de la llamada a push_back
.
Esta garantía es problemática si el constructor de movimientos lanza una excepción.
Cuando el vector
reasigna, le gustaría mover los elementos del búfer anterior al nuevo. Sin embargo, si alguno de esos movimientos lanza una excepción (además del primero), se deja en un estado en el que se ha modificado el búfer anterior y el nuevo búfer aún no contiene todo lo que se supone que debe hacer. El vector
no puede restaurar el búfer anterior a su estado original porque tendría que mover elementos para hacerlo, esos movimientos también podrían fallar.
Así que se estableció una regla para C ++ 11:
Si
T
tiene un constructornoexcept
move, se puede usar para mover los elementos del búfer anterior al nuevo.De lo contrario, si
T
tiene un constructor de copia, se usará en su lugar.De lo contrario (si no hay un constructor de copia accesible), el constructor de movimiento se usará después de todo, sin embargo, en este caso, ya no se otorga la garantía de seguridad de excepción fuerte.
Aclaración: "constructor de copia" en la regla 2 significa un constructor que toma una const T&
, no uno de esos weenie llamados constructores de T&
copia. :-)
Use noexcept
en su constructor de movimiento:
TestClass(TestClass&& other) noexcept { ... }
noexcept
sin una expresión constante como esta es equivalente a noexcept(true)
.
El compilador puede usar esta información para habilitar ciertas optimizaciones en las funciones de no lanzar, así como habilitar el operador noexcept, que puede verificar en el momento de la compilación si se declara una expresión particular para lanzar alguna excepción.
Por ejemplo, los contenedores como std :: vector moverán sus elementos si el constructor de movimiento de los elementos es noexcept, y se copia de lo contrario.
Fuente: http://en.cppreference.com/w/cpp/language/noexcept_spec
NB: Esta es una característica de C ++ 11 . Cierto compilador puede no haberlo implementado todavía ... (por ejemplo: Visual Studio 2012 )