Un árbol binario, su constructor de copia y operador de asignación -- ++ campo con c++11 campo con tree campo con constructor camp codereview Relacionados El problema

A binary tree , its copy constructor and assignment operator


1
vote

problema

Español

Implementé un árbol binario en el siguiente código. El constructor de copia de su nodo y el operador de asignación deben copiarse a sí y a todos sus descendientes. Similitud El destructor de un nodo debe eliminarse y todos los nodos descendieron de ella. La función de impresión simplemente imprime cada nodo en una nueva línea. ¿Cómo puedo intentarlo bastante? Por favor, apunte a errores (si corresponde) y sugerencias.

  #include <iostream>  struct Node {     Node(int);     Node(const Node&);     Node& operator=(const Node&);     ~Node();      Node* left;     Node* right;     int value; };  Node::Node(int v) :left(nullptr), right(nullptr), value(v) {}  Node::Node(const Node& other) :left(nullptr),  right(nullptr),  value(other.value) {     if (other.left != nullptr )     {         left = new Node(*other.left);     }     if (other.right != nullptr)     {         right = new Node(*other.right);     } }  Node& Node::operator=(const Node& other) {     value = other.value;      Node * left_orig = left;     left = new Node(*other.left);     delete left_orig;      Node * right_orig = right;     right = new Node(*other.right);     delete right_orig;      return *this; }  Node::~Node() {     if (left != nullptr )     {         delete left;     }     if (right != nullptr)     {         delete right;     } }  Node* make_copy(Node* other) {     Node * new_node = new Node(*other); // copy constructor invoked     return new_node; }   void print(Node* n) {     if (n == nullptr)     {         return;     }     std::cout << n << std::endl;     print(n->left);     print(n->right);  }   int main() {     Node* n = new Node(1);      n->left = new Node(2);     n->right = new Node(3);      n->left->left = new Node(4);     n->left->right = new Node(5);      n->right->left = new Node(6);     n->right->right = new Node(7);      print(n);     auto nc =  make_copy(n);     print(nc); }   
Original en ingles

I implemented a binary tree in the following code. Its node's copy constructor and assignment operator should copy itself and all its descendents. Similarity a node's destructor should delete itself and all nodes descended from it.The print function just prints each node in a new line. How can I pretty-print it? Please point to errors(if any) and suggestions.

#include <iostream>  struct Node {     Node(int);     Node(const Node&);     Node& operator=(const Node&);     ~Node();      Node* left;     Node* right;     int value; };  Node::Node(int v) :left(nullptr), right(nullptr), value(v) {}  Node::Node(const Node& other) :left(nullptr),  right(nullptr),  value(other.value) {     if (other.left != nullptr )     {         left = new Node(*other.left);     }     if (other.right != nullptr)     {         right = new Node(*other.right);     } }  Node& Node::operator=(const Node& other) {     value = other.value;      Node * left_orig = left;     left = new Node(*other.left);     delete left_orig;      Node * right_orig = right;     right = new Node(*other.right);     delete right_orig;      return *this; }  Node::~Node() {     if (left != nullptr )     {         delete left;     }     if (right != nullptr)     {         delete right;     } }  Node* make_copy(Node* other) {     Node * new_node = new Node(*other); // copy constructor invoked     return new_node; }   void print(Node* n) {     if (n == nullptr)     {         return;     }     std::cout << n << std::endl;     print(n->left);     print(n->right);  }   int main() {     Node* n = new Node(1);      n->left = new Node(2);     n->right = new Node(3);      n->left->left = new Node(4);     n->left->right = new Node(5);      n->right->left = new Node(6);     n->right->right = new Node(7);      print(n);     auto nc =  make_copy(n);     print(nc); } 
           
   
   

Lista de respuestas

2
 
vote
vote
La mejor respuesta
 

Operador de asignación.

El operador de asignación no es 100% seguro de excepción.
No debe modificar el objeto actual mientras aún no ha terminado de hacer la copia.

  Node& Node::operator=(const Node& other) {     value = other.value;      Node * left_orig = left;     left = new Node(*other.left);     delete left_orig;      // You have modified the left side.     // If while makeing the copy of the right side     // it throws an exception (that is caught). Then you     // have an object that is half one thing (new stuff)     // and half the other (old stuff on the right).     Node * right_orig = right;     right = new Node(*other.right);     delete right_orig;      return *this; }   

Debería parecerse más así:

  Node& Node::operator=(const Node& other) {     Note* newleft  = nullptr;     Node* newright = nullptr;      // Do the dangerous stuff in isolation.     // without changing the chaging the current object.     try {         newleft  = new Node(*other.left);         newright = new Node(*other.right);     }     catch(...) {         // If there was a problem         // clean up any temporary objects.         delete newleft;         delete newright;         // Then re-throw         throw;     }     // Now perform the exception safe change of state.     // None of these operations are allowed to throw.     value = other.value;     std::swap(left,  newLeft);     std::swap(right, newRight);      // Now that the object is in a consistent state.     // we can delete the old data.     // Do this last (in other types where the data is not in     // this can potentially throw).     delete newLeft;     delete newRight;      return *this; }   

ahora que parece mucho trabajo duro.
Hay una forma más fácil de lograr exactamente el mismo efecto. Puede usar la copia y el modificador Idiom.

  Node& Node::operator=(Node other)    // Pass by value to generate a copy. {     other.swap(*this);               // Swap the state of this and the                                      // copy we created in `other`     return *this; }                                    // destructor of other now                                      // does the tidy up.   

Destructor

Llamar al eliminar en un nullptr es válido y no hace nada.

  Node::~Node() {     if (left != nullptr )     {         delete left;     }     if (right != nullptr)     {         delete right;     } }   

para que podamos simplificar esto también.

  Node::~Node() {     delete left;     delete right; }   

¿Por qué necesita un 9988776655544335 ?

  Node* make_copy(Node* other) {     Node * new_node = new Node(*other); // copy constructor invoked     return new_node; }   

Es bastante normal llamar directamente al constructor de copia.

Declaración de impresión

Puede hacer que la función de impresión sea un método. También para hacerlo más versátil, debe pasar un flujo al que desea imprimir (por lo que también puede imprimir en archivo). Si desea una versión sin argumento, simplemente haga que el parámetro de transmisión sea predeterminado en std::cout .

  void Node::print(std::iostream& str = std::cin) {     std::cout << value << std::endl;     if (left) {         left->print(str);     }     if (right) {         right->print(str);     } }   

Esto facilita definir el operador de salida para su clase.

  std::ostream& operator<<(std::ostream& str, Node& data) {     data.print(str);     return *this; }   
 

Assignment operator.

The assignment operator is not 100% exception safe.
You should not modify the current object while you have not yet finished making the copy.

Node& Node::operator=(const Node& other) {     value = other.value;      Node * left_orig = left;     left = new Node(*other.left);     delete left_orig;      // You have modified the left side.     // If while makeing the copy of the right side     // it throws an exception (that is caught). Then you     // have an object that is half one thing (new stuff)     // and half the other (old stuff on the right).     Node * right_orig = right;     right = new Node(*other.right);     delete right_orig;      return *this; } 

It should look more like this:

Node& Node::operator=(const Node& other) {     Note* newleft  = nullptr;     Node* newright = nullptr;      // Do the dangerous stuff in isolation.     // without changing the chaging the current object.     try {         newleft  = new Node(*other.left);         newright = new Node(*other.right);     }     catch(...) {         // If there was a problem         // clean up any temporary objects.         delete newleft;         delete newright;         // Then re-throw         throw;     }     // Now perform the exception safe change of state.     // None of these operations are allowed to throw.     value = other.value;     std::swap(left,  newLeft);     std::swap(right, newRight);      // Now that the object is in a consistent state.     // we can delete the old data.     // Do this last (in other types where the data is not in     // this can potentially throw).     delete newLeft;     delete newRight;      return *this; } 

Now that looks like a lot of hard work.
There is an easier way to achieve exactly the same affect. You can use the copy and swap idiom.

Node& Node::operator=(Node other)    // Pass by value to generate a copy. {     other.swap(*this);               // Swap the state of this and the                                      // copy we created in `other`     return *this; }                                    // destructor of other now                                      // does the tidy up. 

Destructor

Calling delete on a nullptr is valid and does nothing.

Node::~Node() {     if (left != nullptr )     {         delete left;     }     if (right != nullptr)     {         delete right;     } } 

So we can simplify this too.

Node::~Node() {     delete left;     delete right; } 

Why do you need a make_copy?

Node* make_copy(Node* other) {     Node * new_node = new Node(*other); // copy constructor invoked     return new_node; } 

It is quite normal to call the copy constructor directly.

Print statement

You can make the print function a method. Also to make it more versatile you should pass a stream to which you want to print (so it can also print to file). If you want a no argument version just make the stream parameter default to std::cout.

void Node::print(std::iostream& str = std::cin) {     std::cout << value << std::endl;     if (left) {         left->print(str);     }     if (right) {         right->print(str);     } } 

This makes it easy to define the output operator for your class.

std::ostream& operator<<(std::ostream& str, Node& data) {     data.print(str);     return *this; } 
 
 
       
       

Relacionados problema

7  Solución alternativa para constructor sobrecargado en PHP  ( Workaround for overloaded constructor in php ) 
Tengo una clase cuyo propósito es mostrar un comentario. Me gustaría poder instanciarlo al pasar un objeto 998877666555443319 template <std::size_t N> cla...

6  Argumentos en los constructores campos a juego  ( Arguments in constructors matching fields ) 
Bloqueado . Esta pregunta y sus respuestas son bloqueadas porque la pregunta es off-topic pero tiene importancia histórica. Actualmente no está a...

45  Objetos instantáneos con muchos atributos  ( Instantiating objects with many attributes ) 
Tengo una clase con bastantes atributos, la mayoría de los cuales se conocen cuando creo una instancia del objeto. Así que paso todos los valores en el constr...

41  Conexión de la base de datos en constructor y destructor  ( Database connection in constructor and destructor ) 
Estoy jugando con diferentes maneras de hacer la interacción de la base de datos en PHP, y una de las ideas con las que he estado jugando está conectando a la...

3  Construyendo una función de distribución acumulada de un histograma  ( Constructing a cumulative distribution function from a histogram ) 
Durante algún proceso de funcionamiento, se acumulará un histograma de valores. Cuando se hace, el CDF se derivará de ella y se utilizará para obtener los c...

2  Subclajusing Uibutton  ( Subclassing uibutton ) 
Necesito SUBCLASS UIButton , sin embargo, el diseño de UIButton hace que esto sea realmente doloroso. Esto se debe a que para crear un botón que realmente ...

5  Conexión de base de datos MySQL en el constructor  ( Mysql database connection in the constructor ) 
Soy un principiante absoluto en PHP OOP en busca del "Santo Grial" de la base de datos de MySQL una vez y reutilizando esta conexión para todo el sitio. cl...

3  Cambiando el estado de otra entidad en el método del constructor  ( Changing the state of another entity into the constructor method ) 
Tengo una clase de reloj que se hereda de Pyglet.clock.clock y se comporta como un singleton. en el método del constructor Tengo la siguiente línea: pygl...

1  Establecimiento de parámetros para un informe de ventas, con valores de booleano y fecha predeterminados  ( Establishing parameters for a sales report with default boolean and date values ) 
La idea es que estoy obteniendo los parámetros de una solicitud 99887766655443311 appendSoldier2 y appendSoldier3 . appendSoldier4 El problema es qu...

7  Constructores: muchos parámetros o ninguno y uso de métodos establecidos  ( Constructors lots of paramters or none and use of set methods ) 
En su opinión, cuál es la mejor manera de construir un objeto, dado los dos ejemplos a continuación: Opción 1: muchos parámetros private string firstna...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos