Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Constructores para la clase AES.
- * Para simplificar, estoy suponiendo que solo hay un arreglo
- * miembro (la llave), en lugar de tres.
- */
- /*
- * Constructor normal: recibe el tamaño de llave y reserva
- * el espacio para el arreglo de la llave.
- */
- AES::AES(int tamanioLlave)
- : mTamanioLlave {tamanioLlave},
- mLlave {new unsigned char [mTamanioLlave]}
- {
- }
- /*
- * Constructor por copia: crea un nuevo objeto a partir de
- * la referencia dada.
- *
- * Se usa delegación de constructores: primero se manda a llamar
- * al anterior, este reserva memoria, y luego aquí se hace la
- * copia solamente
- *
- * Es importante notar que esta operación toma O(n) pasos en
- * llevarse a cabo, ya que es una copia. Perdón por escribir lo obvio:
- * al terminar esta operación tienes dos objetos AES con lo
- * mismo.
- */
- AES::AES(const AES& original)
- : AES{original.mTamanioLlave}
- {
- memcpy(mLlave, original.mLlave, mTamanioLlave);
- }
- /*
- * Asignación por copia.
- */
- AES& AES::operator=(const AES& original)
- {
- mTamanioLlave = original.mTamanioLlave;
- delete[] mLlave;
- mLlave = new unsigned char[mTamanioLlave];
- memcpy(mLlave, original.mLlave, mTamanioLlave);
- return *this;
- }
- /*
- * Constructor por movimiento. Igual que la copia, crea
- * un nuevo objeto a partir de la referencia dada; la diferencia
- * es que esta referencia es de tipo rvalue (una referencia a algo
- * que puede ir del lado derecho en una expresión). Creo que no
- * acabaría si intento entrar en detalles sobre las diferencias entre
- * lvalue y rvalue. Lo importante aquí es que este constructor
- * tiene la garantía de que el objeto fuente (original) ya no se va
- * a volver a usar, por lo tanto, en lugar de copiar el contenido
- * del arreglo, simplemente hay que hacer que el apuntador apunte
- * (valga la redundancia) a la misma dirección de memoria.
- *
- * Importante: esta operación es constante O(1). Al final, queda un
- * nuevo objeto AES igual al anterior, y uno vacío, que ya no se
- * puede usar.
- *
- * En la lista de inicialización se inicializa el tamaño de llave y
- * el apuntador interno (ojo, solo se actualiza el apuntador, no
- * se copia el contenido de todo el arreglo). En el cuerpo se deja
- * inutilizable al objeto anterior. Lo importate es hacer nullptr
- * su arreglo interno, para que cuando se llame al desctructor,
- * este no libere la memoria (ya que esa memoria es la misma que
- * está ocupando el nuevo objeto).
- */
- AES::AES(AES &&original)
- : mTamanioLlave {original.mTamanioLlave},
- mLlave {original.mLlave}
- {
- original.mTamanioLlave = 0;
- original.mLlave = nullptr;
- }
- /*
- * Asignación por referencia a rvalue (por movimiento).
- */
- AES::operator=(AES &&original)
- {
- mTmanioLlave = original.mTamanioLlave;
- delete[] mLlave; /* Importante: liberar memoria anterior. */
- mLlave = original.mLlave;
- original.mTamanioLlave = 0;
- original.mLlave = nullptr;
- return *this;
- }
- /*
- * Destructor. Libera la memoria.
- */
- AES::~AES()
- {
- delete[] mLlave;
- }
- /*
- * Ahora, algunos ejemplos de cuando se manda llamar por defecto
- * a cada constructor y a cada operación de asignación:
- */
- AES funcionDePrueba(AES argumento);
- int main ()
- {
- /* Constructor normal: */
- AES prueba {5};
- /* Constructor por copia: */
- AES pruebaDos {prueba};
- /* Constructor por movimiento: */
- AES pruebaTres {std::move(prueba)};
- /* Asignación por copia: */
- pruebaTres = pruebaDos;
- /* Asignación por movimiento: */
- pruebaDos = std::move(pruebaTres);
- /* Aquí hay varios:
- * Primero, se utiliza el constructor por copia para poner
- * el contenido de pruebaDos en el contexto de la función.
- * Segundo, se utiliza el constructor por movimiento cuando
- * la función regresa. */
- AES pruebaCinco = funcionDePrueba(pruebaDos);
- }
- /*
- * std::move regresa un rvalue del objeto dado (el nombre
- * no es muy descriptivo).
- *
- * Es importante notar como la operación por movimiento solo
- * se utiliza de forma normal cuando las funciones regresan;
- * para llamarla de forma explícita hay que utilizar std::move.
- *
- * Una vez que se conoce cómo funcionan ambas operaciones,
- * se pueden ocupar para hacer diseños más eficientes. En particular,
- * siempre que se esté seguro de ya no ocupar un objeto, hay que
- * utilizar la operación de movimiento (evitando hacer copias
- * inútiles). Por ejemplo, considere estas dos versiones de
- * una operación de intercambio (swap):
- */
- void swapUno(AES uno, AES dos)
- {
- AES temporal = uno;
- uno = dos;
- dos = temporal;
- }
- void swapDos(AES uno, AES dos)
- {
- AES temporal = std::move(uno);
- uno = std::move(dos);
- dos = std::move(temporal);
- }
- /* Ambas hacen lo mismo, sin embargo la primera ocupa copias
- * y la segunda movimientos: la primera está en el orden de
- * O(3 * n), mentras que la segunda solo actualiza apuntadores
- * O(3). Puede parecer una ventaja un tanto menor, sin embargo
- * esto depende totalmente del tamaño del arreglo interno; este
- * puede tener tanto 10 elementos, como 10 millones, en cuyo caso
- * la primera versión del swap es totalmente ineficiente.
- */
Add Comment
Please, Sign In to add comment