He hecho una clase de plantilla para un nodo en una lista enlazada y estoy tratando de tener la salida de su contenido a un flujo de salida por sobrecarga de <<. Sin embargo, mi código actual:

#include <iostream>
using namespace std;
template<class NType> class Node;
template<class NType>
class Node {
private:
void deletePointer(NType* p);
public:
NType data;
Node *prev, *next;
template<typename T>
struct is_pointer { static const bool value = false; };
template<typename T>
struct is_pointer<T*> { static const bool value = true; };
Node();
Node(NType data);
~Node();
};  
int main() {
Node<int> *n1 = new Node<int>();
Node<int> *n2 = new Node<int>(10);
std::cout << "Node 1: " << n1 << std::endl;
std::cout << "Node 2: " << n2 << std::endl;
}
template<class NType> inline std::ostream & operator << (std::ostream& out, const Node<NType> &node){
out << node.data;
return out;
}
template<class NType> inline Node<NType>::Node()
:data(NULL), prev(NULL), next(NULL)
{
}
template<class NType> inline Node<NType>::Node(NType data)
:data(data), prev(NULL), next(NULL)
{
}
template<class NType> inline Node<NType>::~Node(){
if(is_pointer<NType>::value){
deletePointer(&data);
} else {
return;
}
}
template<class NType> inline void Node<NType>::deletePointer(NType* p){
delete p;
}

Salidas de los lugares de la memoria en lugar de los datos dentro de los nodos. Esto sucede con los tipos primitivos como int y el estilo, como si no supiera de qué tipo de datos se encontraba en el NType contenedor.

Node 1: 0x741010
Node 2: 0x741030
Node 3: 0x741070
Node 4: 0x741090

He intentado usar typename en lugar de class pero todavía no dice… ¿hay alguna manera de forma dinámica averiguar qué tipo de la plantilla que se está utilizando y yeso o algo antes de la inserción? Sé que puedo hacer un montón de código redundante para todos los primitivos, pero que parece derrochadora e innecesaria.

Si esto ayuda, estoy compilando en Arch Linux x64 con GCC v4.6.2 20111223

Editar: a partir de una gran cantidad de personas están mencionando. También he intentado poner el fuera de clase como un amigo y como independiente de la función de ninguno de los cuales funciona debido a que el flujo de salidas de la dirección en lugar de los datos en sí, independientemente de dónde lo puse. No hay datos privados de los valores al ser consultado por lo que está bien para no ser un amigo.

Editar:
Caso de prueba: http://ideone.com/a99u5
También se actualiza la fuente de arriba.

Editar:
Agrega el resto de mi código para ayudar a Aarón en su comprensión del código.

  • De la manera que lo he hecho requiere SomeNode << cout que claramente no es lo que quería decir. ostream& operator<< debe convencionalmente ser siempre una función libre porque desea que la ostream en el lado izquierdo. La fijación de que no va a resolver su problema necesariamente, pero…
  • Producir un testcase, por favor. ideone.com
InformationsquelleAutor TechWiz | 2012-01-11

5 Comentarios

  1. 7

    Su código declara la operator<< como un miembro de la función, así que en realidad llevar a la this puntero como primer argumento y ostream como segundo. En su lugar debe ser una función libre:

    template<class NType> class Node {
    public:
    NType data;
    Node *prev, *next;
    };
    //Note how this is declared outside of the class body, so it is a free function instead of a memberfunction
    template<class NType> inline std::ostream& operator<<(std::ostream& out, const Node<NType>& val){
    out << val.data;
    return out;
    }

    sin embargo, si su operator<< necesidades de acceso a los datos que necesita para declarar como una función friend lugar:

    template<class NType> class Node {
    public:
    NType data;
    Node *prev, *next;
    friend std::ostream& operator<<(std::ostream& out, const Node& val){
    out << val.data;
    return out;
    }
    };

    Ahora para su salida: Si su operator<< se invoca el compilador sabe el tipo de NType y hacer lo correcto cuando la transmisión, el data miembro. Sin embargo, desde su operator<< no debe haber trabajado (como está escrito) y parece dar memoryaddresses como de salida, supongo que tienes algo como la siguiente:

    Node* n = new Node();
    std::cout<<n;
    //When it should be:
    std::cout<<*n;

    Ahora sólo por curiosidad bien: ¿por Qué se está implementando lo que se ve curiosamente como una lista enlazada a sí mismo en lugar de simplemente utilizar std::list?

    Edición:
    Ahora que podemos ver el testcase parece que las suposiciones acerca de cómo la operator<< fue llamado fue correcta. La salida debe ser cambiado a:

    std::cout << "Node 1: " << *n1 << std::endl;
    std::cout << "Node 2: " << *n2 << std::endl;

    para invocar realmente la operator<< para Node, en lugar del genérico para T*

    • Soy consciente de que hay varias representaciones de una lista enlazada en la interwebs y en las bibliotecas estándar, pero yo sólo estaba tratando de poner esto así que puede controlar la manera en que funciona para el bien de mi comprensión personal de mi propia estructura de datos.
    • … Wow un tonto error… me siento como un novato jaja. Gracias por su amabilidad.
    • No mantenga esa sensación queridos, que va a venir de nuevo a usted muy pronto de nuevo. 😉
  2. 3

    ¿Por qué se va a añadir una template<class NType> en frente de su ya plantilla de método de clase?

    Por lo general, la mejor manera de sobrecarga del operador<< es hacer a un amigo:

    template<typename NType>
    friend std::ostream& operator<<(std::ostream& out, const Node<NType>& node)
    {
    out << node.data;
    return out; 
    }

    Editar: Para responder a los comentarios más abajo, esto es a lo que me refiero:

    Cuando se define una clase de plantilla en el encabezado, no se que plantilla para las funciones miembro de la clase:

    template<typename T>
    class Foo
    {
    T some_data;
    void member_fn();
    }

    Esto es necesario cuando se está declarando un amigo de la función, sin embargo:

    template<typename NType>
    class Node 
    {
    public:
    NType data;
    Node<NType> *prev, *next;
    //Note the different template parameter!
    template<typename NType1>
    friend std::ostream& operator<<(std::ostream& out, const Node<NType1>& node);
    };

    Aplicación de la que luego se convierte en la de arriba template<typename NType> std::ostream& operator<<(std::ostream& out, const Node<NType>& node) aplicación.

    • No sigo. También puso template<typename NType> delante de su clase? También devuelve direcciones. Me estoy perdiendo algo aquí?
    • Lo siento, tengo un poco descuidado con mi copia y pegar. La función se ha declarado en la definición de la clase y se definen fuera de la clase. Pero en aras de la brevedad que se trasladó a la clase. Lo que era claramente una idea estúpida como lo crea más confusión que tendría que parece.
  3. 1
    template <class NType>
    class Node
    {
    public:
    NType data;
    Node *prev, *next;
    };
    template <class NType>
    inline std::ostream& operator<<(std::ostream& os, const Node<NType>& n)
    {
    return out << n.data;
    }
    • Yo he probado anteriormente (y de nuevo una vez que re-menciona sólo en el caso), pero el mismo trato.
  4. 0

    Tienes la impresión de las direcciones de los nodos, no los nodos:

    int main() {
    Node<int> *n1 = new Node<int>();
    Node<int> *n2 = new Node<int>(10);
    std::cout << "Node 1: " << n1 << std::endl;
    std::cout << "Node 2: " << n2 << std::endl;
    }

    n1 y n2 son punteros, no a los objetos. Usted debería haber escrito:

    int main() {
    Node<int> *n1 = new Node<int>();
    Node<int> *n2 = new Node<int>(10);
    std::cout << "Node 1: " << *n1 << std::endl;
    std::cout << "Node 2: " << *n2 << std::endl;
    }

    o:

    int main() {
    Node<int> n1();
    Node<int> n2(10);
    std::cout << "Node 1: " << n1 << std::endl;
    std::cout << "Node 2: " << n2 << std::endl;
    }
    • Sí ese era el problema gracias. Dang Java tryna inculcar terribles hábitos en mí.
  5. 0

    Otros han señalado que usted necesita para utilizar cout << *n1 en lugar de cout << n1, pero hay otro importante error en el código.

    Su destructor incluye una llamada deletePointer(&data); donde data es un miembro de la clase. Esto es peligroso. Si esta línea de código nunca fue ejecutado, a continuación, el programa probablemente accidente. data es sólo una pequeña parte del objeto señalado por this, tratando de borrar es como tratar de eliminar un solo elemento en una matriz de enteros.

    Node<int> *n1 = new Node<int>();
    delete n1; //this makes sense. deleting the same thing that was new'ed
    Node<int> *n1 = new Node<int>();
    delete &(n1->data); //BUG

    Probablemente debería cambiar su diseño mucho y simplemente quitar el código especial para el puntero. Qué esperas para escribir código como: Node<int*> *n2 = new Node<int*>( new int ); ? Y si es así, ¿cómo quieres que se comporte?

    Si NType en realidad era un tipo de puntero, como int*, entonces tendría sentido hacer deletePointer(data), pero nunca sentido hacer deletePointer(&data).

    • El deconstructor cheques para el puntero de datos antes de llamar a deletePointer, por lo que si los datos no es un puntero, el deconstructor no llamar a la función. También decirle a eliminar para eliminar una variable de miembro de fuera de su propia deconstrucción código parece algo que debería error, independientemente de si se construye correctamente el código o no. Que apenas se parece como la mala programación. El deconstructor es también no se supone que se llama directamente, sino que debe ser parte de la recolección de basura cuando el programa se cierra Y estos nodos son parte de algo más grande, así no sólo de su aún destinada a ser utilizada de esa forma.
    • eso no es correcto, lo siento. Vamos a ser claros. Incluso si data se un tipo de puntero, entonces debe ser eliminado con delete data, no con delete &data.
    • También @TechWiz, esto es C++, Java no. Usted dijo: «… pero debe ser parte de la recolección de basura».
    • is_pointer<NType>::value será verdadera si y sólo si X es un tipo de puntero. Por ejemplo, es NType es int * entonces es un tipo de puntero. Además, si NType es int *, entonces data será declarado como int * data; Como resultado, usted tendrá que llamar delete data para eliminar la int señalado por los datos.
    • Yo siento que usted está equivocado. Funciona muy bien, pruébalo por ti mismo. El programa ni se bloquea no se crea una pérdida de memoria. Sé que este no es java pero por defecto, el deconstructor se llama en todos los no-valores de puntero cuando el programa se lleva a cabo la ejecución de la medida en que haya definido. Por favor, observar con más cuidado en el código, la estática struct is_pointer siempre será falsa en el incumplimiento de los tipos de puntero como NType no va a ser un puntero a menos que, por supuesto, un tipo de puntero. También el código para deletePointer() falta en mi post, pero me gusto lo de editar mi pregunta a incluir.
    • Veo lo que quieres decir sobre el error, sin embargo, que sólo ocurriría si data fueron un explícita puntero, que no lo es. En su lugar es una plantilla para todos los tipos de datos, de modo que si se tratase de un puntero, sería un implícito puntero de modo que usted no puede llamar a eliminar directamente en ella a menos que usted sea un cast a un puntero como lo hizo (lo que hará la struct is_pointer falsa en el proceso) o enviarlo a una función alternativa para eliminar el puntero de haber sido emitidos durante la llamada (como yo he hecho) hacer explícito puntero que puede ser eliminado.
    • Yo estaría interesado en el código de deletePointer por favor. Tal vez usted hizo void deletePointer(NType* p) { delete *p; }. O tal vez es void deletePointer(NType* p) { delete p; } lugar?
    • Ya está publicado anteriormente en mi pregunta, al final del fragmento de código. Es el último de los dos métodos.
    • He probado el código y se cuelga con los tipos de puntero – a Ver esto en ideone. Lo importante es que el caso de prueba actual no llamar al destructor. En C++ debe ser muy explícito sobre la liberación de memoria. En C++, si se crea un objeto con new es no automáticamente se destruye cuando el programa termina. Por lo tanto, he añadido delete n1; y delete n2;. Cada new debe haber una coincidencia de delete en el código. Además, he incluido un examen de los punteros; he cambiado de n2 a ser Node<int*> *n2 = new Node<int*>();.
    • Ah ya veo lo que quieres decir. Pero no es como he borrado de mi puntero, es el hecho de que he borrado de mi puntero que está causando el problema. Hay un «doble eliminación» de la variable de datos que es raro ya que estaba causando fugas anteriores porque GCC se negó a limpiarlo correctamente. Gracias por la captura de ese, habría sido muy embarazoso tener que publicar ese error aquí más tarde =P
    • También C++ tiene la recolección de basura, es muy primitivo. De lo contrario, tendríamos que llamar a eliminar en cada variable que creamos y que claramente no es el caso. La razón por la que estaban teniendo problemas para explicar este problema, para mí, es debido a que se está refiriendo a mí no eliminar mi n1 y n2 variables en mi principal que causa una evidente pérdida, acabo de escribir el código de forma rápida para hacerlo y se olvidó de los 2 borrar líneas. Yo no entendía lo que estaba diciendo acerca de llamar a delete &(n1->data); porque eso es como llamar a int x; delete &(x); que definitivamente es gunna accidente.
    • No es una doble eliminación. Es una corrupción de memoria. Aquí es mucho más sencillo ejemplo que muestra el problema en ideone
    • Que no es el mismo tipo de error. Su segundo ideone caso de prueba se bloquea debido a que están obligando a la eliminación de la data miembro de la variable que está implícito un puntero, y por lo tanto la razón se debe convertir la variable antes de eliminarlo. Por favor, por citar un ejemplo donde tiene sentido el ir y eliminar una variable miembro desde fuera de la propia clase. Que simplemente no parece adecuada la metodología de la programación. También, dado que este es un nodo de datos en una lista enlazada, uno podría eliminar toda la entidad no los datos dentro a menos que uno quería reutilizar el recipiente, pero por que me hizo un swap método.
    • Mirando de nuevo a la primera caída, puedo hacer un pequeño cambio que demuestra que no es una doble eliminación. En la versión 2 he hecho un cambio muy pequeño. He cambiado el orden en que las variables están listadas; ahora prev y next aparecen antes de data. Ahora se bloquea con *** glibc detected *** ./prog: free(): invalid pointer: 0x08901020 ***
    • Para evaluar sus preocupaciones con el segundo ideone caso de prueba, que no elimina el miembro de datos. Desde que se hizo el Node mantener un int*, el miembro data es un puntero para hacer delete &(a->data); es la eliminación de un puntero a un puntero que no existen en la memoria. Para eliminar el miembro que uno hace delete a->data;, como ya es un puntero. Aquí es un caso de prueba para esto: ideone.com/uBinY
    • Me puede explicar todos estos detalles, y yo sé por qué el error cambia de «doble eliminación» a «puntero no válido». Lo siento si fui grosero antes, pero estoy bastante seguro de que mi conocimiento de estos temas.
    • Purgar el deletePointer() función y el código funciona bien, como he dicho antes, debido a que es intentar eliminar un ya eliminado miembro de la variable después de deletePointer() se llama. La razón de su doble eliminación es porque data es un NType tipo no NType*. Si data fueron un NType* entonces sí, sería completamente correcto. Sin embargo, un defecto se llama deconstructor sobre todo no los tipos de puntero, incluyendo NType. Aquí un ejemplo en el que más de cerca que representa a mi actual codebase para probar mi punto: ideone.com/eTdng
    • Aquí hay dos ejemplos más que demostrar el trabajo y roto comportamiento. Puedo ver que no te gusta la eliminación de elementos de fuera, así que lo he cambiado. En la versión 4a el código se bloquea. En versión 4b. En estos ejemplos que he creado un nuevo tipo, llamado MyOwnType, que imprime un mensaje de ayuda a medida que se destruye. Entonces he cambiado principal por lo que se crea uno de estos objetos y los almacena en un Node<MyOwnType*> *n3 = new Node<MyOwnType*>( ..... . En la segunda versión me fijo por el uso de delete *p; en lugar de delete p.
    • Dentro de Node<int*>, NType es un tipo de puntero. Que no es negociable y por lo tanto usted debe eliminar con delete data; en algún momento.
    • ¿Tienes un terminal de linux disponibles para usted? Revisando mi código con valgrind no tengo fugas de ningún tipo y sin embargo su código en efecto, la fuga, como usted dice. Creo que este es un problema entre los punteros a tipos primitivos con defecto deconstructors y punteros a clases. Veo que el problema que se esta hablando, y la razón por la que no tiene sentido es porque mi código de no producir fugas con punteros a int* pero int* solo crearía una fuga. Estoy bastante seguro de que esta es la razón por la que he creado el deletePointer() método, sino que fue arrojado fuera por int* no la creación de fugas.
    • Aquí hay tres ejemplos basados en su código más reciente. En el primera versión, simplemente tengo la MyOwnType y han creado Node<MyOwnType*> *n2 a la tienda. Como era de esperar, este funciona bien. En el segunda versión el destructor ~Node incluye una llamada a eliminar el objeto puntiagudo para ser data; esta versión se ejecuta correctamente, puede ver el mensaje de ayuda «estoy siendo destruidas. Bye Bye!». Finalmente, en la tercera versión he utilizado delete &data. La tercera versión se estrelló con el «puntero no válido error».
    • Hay una muy particular, razón por la que usted más reciente código de ideone.com/eTdng no reporta cualquier fuga. Te han llamado delete n1 y delete n2, lo cual es bueno, por supuesto. Pero también, no especifica ningún valor inicial, en los soportes en new Node<int*>();. Como resultado, el data miembro estaba a un puntero null – muy similar a la null de referencia en Java. Como resultado, usted no ve ninguna de las fugas. (.. a continuación)
    • …. sin embargo, si usted hizo un pequeño cambio y se hizo new Node<int*>(new int); entonces usted recibirá una fuga. Usted debe tratar de valgrind en una versión de su código, incluyendo una llamada a ` MyOwnType *p = new MyOwnType; Nodo<MyOwnType*> *n2 = new Nodo<MyOwnType*>(p);` – usted verá una pérdida de memoria.
    • En resumen, int* creará fugas si se pasa un puntero a una construidos int. Usted dijo «pero que fue arrojado fuera por int* no crear fugas». Trate de new Node<int*>(new int);
    • Oh, yo soy una grave idiota… no me di cuenta de que yo era eliminar el puntero yo estaba en la alimentación de la Node en otros lugares. Sí, crea una fuga de ahora. Ahora mi problema es que GCC no me permite el uso de punteros no en mi nodos debido a que se trata de ser «inteligente» y deconstruir una int pero omite la comprobación de punteros completamente.
    • Bueno. Tengo una solución para eso. ideone.com/waYAw. Aquí hay una versión de deletePointer que correctamente puede funcionar si está tratando con un tipo de puntero, y se hará un llamado a delete si es apropiado. De nuevo, tenga en cuenta que el «yo estoy siendo destruidas. Bye Bye!» aparece el texto, lo que confirma que la llamada a eliminar es la correcta.
    • Bien, gracias por su amabilidad. Este es probablemente el más largo hilo de comentarios nunca, pero sí, ha sido muy educativo. Yo creo saber más acerca de los punteros de lo que me gustaría ahora jaja. Agradezco que pegue la vuelta para poder explicar esto a mí y me disculpo por nunca dudar de usted (a través de malentendidos).
    • No hay problema. Yo podría haber hecho más fácil la vida para mí mismo, pero dado menos, pero mejor, las respuestas! Ver en StackOverflow!

Dejar respuesta

Please enter your comment!
Please enter your name here