Declare as an attribute, a pointer to my same type of variable in C ++

1

My problem is this: I have a class Nodo , which has as an attribute an array of pointers to to the same type (as to make a tree) and then I have a daughter class NodoAvl that must inherit that attribute but the pointers must point to the same type, that is, point to NodoAvl . Maybe with my code I can explain them better:

#include<iostream>
using namespace std;

template <class T>
class Nodo
{
    protected:

    T d;

    // Aquí necesito punteros a mi mismo tipo
    // así, Nodo tiene punteros a Nodo
    // y NodoAvl tiene punteros a NodoAvl

    "mi_mismo_tipo" *h[2] = {NULL};

    public:

    Nodo(T dato = T()) { d = dato; }
};

template <class T>
class NodoAvl : public Nodo<T>
{
    protected:
    int alt = 0;
    public:
    NodoAvl (T dato = T()): Nodo<T>(dato) {}
    int altu () {return alt;}
};

Will there be any way to do this? Thanks in advance.

    
asked by Ali Rojas 05.11.2016 в 20:33
source

3 answers

0
template <class T>
class Nodo
{
protected:
    T d;
    Nodo *h[2] = {NULL};

public: 
    Nodo(T dato = T()) { d = dato; }
};

Even if the class Nodo is an incomplete class in the declaration point of h , because it is still being processed, you can always declare pointers, references and functions that receive or return objects by value of this incomplete type. That is, this code compiles (although I have not tried it):

#include <iostream>

class A;

A* a = nullptr;
// A* a2 = new A; // [†]

struct B
{
   A& ra;  // [†2]
};

A f(A);
A& f(A&); // [†3]

// A& ra = *a; // [†4]

int main()
{}

[†]: That does not compile because, even if it is valid to declare pointers to an incomplete type, you can not create any object of that type when not knowing yet what builders you have, and above all, you do not yet know the size of A ( sizeof(A) ).

[† 2]: The case A& I have exemplified as an attribute and not as a local or global variable because you can not declare the reference as a variable (be it global or local), since references must be mandatorily defined in your declaration (for example, ra = *a ), that is, you can not declare a reference, and assign a value to it later. However, since A is an incomplete type, you have no way to "get" the object you want to reference. That is, *a would not compile by A is an incomplete type and we do not know what an object of type A is.

However, you can declare it as an attribute, since there you are not forced to initialize it. That will necessarily happen in the constructor, which you could not implement yet until A is not defined.

[† 3]: Function parameters are the only places where you can declare objects of an incomplete type (and not just references or pointers), since, at that point, you do not need to know its size ( sizeof(A) ) nor its content. Of course, you could not implement the function until A is defined later in the code and copy constructors are already available, etc.

[† 4]: Again, *a can not be applied if you still do not know A . I do not know exactly the reasons, but usually it's usually because to perform that kind of actions the compiler needs its sizeof .

    
answered by 06.11.2016 в 03:00
0

The response from Peregring-lk is valid but I would like to add something to it.

It is common in cases like this to declare nested types 1 in the class for ease of use:

template <class T>
struct Nodo
{
    // "Atajo" al tipo interno del nodo
    using value_type = T;
    // "Atajo" a la clase plantilla
    using my_class = Nodo<value_type>;

    Nodo(value_type dato = value_type()) { d = dato; }

protected:
    value_type d;
    my_class *h[2] = {nullptr};
};

You can see that my_class is an alias of Nodo<value_type> (where value_type is an alias of T ) which is precisely what you ask for: my same variable type .

You can extend this practice to class NodoAvl :

template <class T>
struct NodoAvl : public Nodo<T>
{
    using value_type = T;
    using my_node = Nodo<value_type>;
    using my_class = NodoAvl<value_type>;

    NodoAvl (value_type dato = value_type()): my_node(dato) {}
    int altu () {return alt;}
protected:
    int alt = 0;
};

Following this pattern facilitates some uses and / or makes the code slightly more readable, if you had a function to obtain a node:

struct NodoAvl : public Nodo<T>
{
    using value_type = T;
    using my_node = Nodo<value_type>;
    using my_class = NodoAvl<value_type>;

    // Un poco mas claro que
    // const Nodo<T> &GetNode() const
    const my_node &GetNode() const;

    ...
 };

It also allows you to know the type T , of which you had no knowledge once the template was instantiated:

template <class T>
struct Nodo
{
    using value_type = T;
    using my_class = Nodo<value_type>;

    value_type GetDato() const;

    ...
};

using NI = Nodo<int>;
using NS = Nodo<std::string>;

NI ni; NS ns;

// Una vez hecho el alias o typedef, pierdes la "pista" del
// tipo subyacente del Nodo, a no ser que guardes el tipo
// en un tipo anidado.
NI::value_type i = ni.GetDato();
NS::value_type s = ns.GetDato();

1 You can see examples in: Stl containers such as map or list . Utilities such as initializer_list or pair . This pattern is also present in bookstores like boost or other open source libraries.

    
answered by 07.11.2016 в 10:59
0

The guy can not change magically and miraculously. What you can do is pull polymorphism:

struct A
{
  virtual void func()
  { std::cout << "A\n"; }
};

struct B : A
{
  void func()
  { std::cout << "B\n"; }
};

A* a = new A;
A* b = new B;

a->func(); // Imprime A
b->func(); // Imprime B

Using this feature, the only thing you have to guarantee is that the class NodoAv1 fills the array with elements of type NodoAv1 .

Ok, now how can you access the proper elements of NodoAv1 if the pointer is of type Nodo ?

struct A
{
  virtual void func()
  { std::cout << "A\n"; }
};

struct B : A
{
  void func()
  { std::cout << "B\n"; }

  void func2()
  { std::cout << "func2\n"; }
};

A* b = new B;
b->func(); // Imprime B
b->func2(); // ERROR!!!

The solution is to use some of the available conversion systems. In this case, since you know that in NodoAv1 the pointers are going to be type NodoAv1 , you can choose to use static_cast :

A* b = new B;
b->func(); // Imprime B
static_cast<B*>(b)->func2(); // OK

To give you a more concrete example it would be necessary that you complete the classes of your example to reflect some type of use of the arrangement.

    
answered by 03.03.2017 в 21:03