C++ Datentypen in einer Variablen speichern?

2 Antworten

Vom Beitragsersteller als hilfreich ausgezeichnet

Mach doch einfach eine Liste, der du als Template-Parameter den Typ übergibst.

Oder du erstellst für jeden Datentyp eine eigene Subklasse und speicherst die gemeinsame Baseclass in der Liste.


LukasZander 
Beitragsersteller
 13.11.2022, 16:37

Vielen Dank.

Wie genau geht das?

Ich stelle mir das aktuelle so vor:

class boundList
{

    struct node{ node *prev{nullptr}; void *elem{nullptr}; node *next{nullptr}; };

    node anchor;
    node *_Last;
public:
    template<typename node1_t>
    boundList(node1_t *f_elem)
    {
        anchor.elem = f_elem;
        _Last = &anchor;
    }

    template<typename _Item>
    void append(_Item* n_elem)
    {

        _Last->next = new node{_Last, n_elem};
        _Last->next->prev = _Last;
        _Last = _Last->next;
    }

    template<typename _Item>
    void remove(_Item *r_elem)
    {
        node *tmp = &anchor;

        if (r_elem == anchor.elem)
        {
            anchor = *tmp->next;
            anchor.prev = nullptr;
            return;
        }

        while (tmp != _Last)
        {

            if (tmp->elem == r_elem)
            {
                tmp->prev->next = tmp->next;
                tmp->next->prev = tmp->prev;
                delete tmp;
            }

            tmp = tmp->next;

        }
        if (r_elem == _Last->elem)
        {
            _Last = _Last->prev;
            tmp->prev->next = nullptr;
            delete tmp;
        }

    }


    size_t size()
    {
        size_t c{};

        node *tmp = &anchor;
        while (tmp != _Last)
        {
            tmp = tmp->next;
            c++;
        }
        return c + 1;
    }


    template<typename _OutType>
    _OutType get(const int index)
    {
        int i = 0;

        node *tmp = &anchor;

        while (i < index)
        {
            tmp = tmp->next;
            i++;
        }
        return *static_cast<_OutType*>(tmp->elem);
    }
    template<typename _OutType>
    void get(_OutType* _Obj, const int index)
    {
        int i = 0;

        node *tmp = &anchor;

        while (i < index)
        {
            tmp = tmp->next;
            i++;
        }
        *_Obj = *static_cast<_OutType*>(tmp->elem);
    }
     ~boundList()
     {
         node *tmp = anchor.next;
         node *next;

         while (tmp != nullptr)
         {
             next = tmp->next;
             delete tmp;
             tmp = next;
         }


     }
};
0
Destranix  13.11.2022, 16:39
@LukasZander

Was davon?

Ersteres im Prinzip:

template<T>
struct node{
    node *prev{nullptr};
    T *elem{nullptr};
    node *next{nullptr};
};
0
LukasZander 
Beitragsersteller
 13.11.2022, 16:40
@Destranix

Aber wenn ich node jetzt instanziiere muss ich den typen kennen und der soll ja von mal zu mal unterschiedlich sein

0
Destranix  13.11.2022, 16:41
@LukasZander

Du hast deinen Code erst im Nachhinein hionzugefügt. Da würde man das template zum Listentyp hinzufügen.

Und ja, na klar musst du den Typ dabei angeben.

0
LukasZander 
Beitragsersteller
 13.11.2022, 16:51
@Destranix

Dann kann ich aber nur eingebaute Datentypen abdecken und nicht Benutzerdefinierte Klassen, oder?

0
Destranix  13.11.2022, 16:53
@LukasZander

In der ersten Lösung, das was ich dir mit std::function verlinkt habe, können auch benutzerdefinierte Typen abgedeckt werden.

0
LukasZander 
Beitragsersteller
 13.11.2022, 16:54
@Destranix

Ok, ich muss jedoch zugeben, dass ich da bisher nur bahnhof verstanden habe

0
Destranix  13.11.2022, 16:59
@LukasZander

Im Prinzip hast du einfach nur eine Baseclass, die du in der Liste speicherst. Für jeden neuen Typ, den du speichern möchtest, erstellst du eine neue Klasse, die von der Baseclass erbt und speicherst diese in der Liste.

Das erstellen der neuen Klasse geschieht automatisch via Templates und einer Allokation auf dem Stack:

struct node_base {
   virtual ~node_base() {}
};
template <typename T>
struct node : node_base {
   T element;
   node(T element) : element(element) {}
};

Und in deiner Liste hast du dann zum Beispiel:

template<typename T>
void add(T element){
    this->append(new node(element));
}
0
Destranix  13.11.2022, 17:06
@LukasZander

Es sei aber anzumerken, dass dir das nur bedingt etwas bringt, denn du kannst nur Funktionen der Baseclass aufrufen oder du müsstest den Typ des jeweiligen Eintrags kennen um das zurück zu casten.

0
LukasZander 
Beitragsersteller
 13.11.2022, 17:07
@Destranix

Ok, das seh ich grad. Außerdem speichert er einen einheitlichen Datentypen

0
Destranix  13.11.2022, 17:08
@LukasZander

Genau.

Je nachdem, was du machen möchtest, musst du dann entweder ein einheitliches Interface implementieren oder auf std::variant oder dergleichen zurückgreifen.

0

Templates wären da eine möglichkeit.

An sich ist denke ich die Datenstruktur keine sinnvolle. Weil man dann ja im Endeffekt wissen muss welches Glied in der Kette welchen Typ hat. Oder eben immer raten muss welchen Typ diese Liste hat.

Ich würde das ganze warscheinlich wenn ich so etwas überhaupt machen wollen würde. Innerhalb der Listenelemente Kapseln.

Sprich: ich hab ne Klasse Chainlistelement. Die halt jeweils ne Referenz auf das vorherige oder nächste listen element.

Und hat gleichzeitig auch ein Value Feld welches den eigentlichen wert hält.

So haste die Liste und ihre Datentypen schonmal getrennt.

Hier kann man dann ggf. Mit Templates arbeiten. Wenn du halt richtig geraten hast und die value sich casten lässt kriegste die raus. Ansonsten halt null.

Alternativ kannste die value auch einfach vom Typ object lassen und die lässt den Benutzer casten.

Alternativ: kannste auch mehrere value felder haben für die unterstützen Typen.

Oder du leitest für jede unterstützte value ne eigene element Klasse ab. Z.b. die Klasse InegerChainElement für eben integer Werte.

Im Endeffekt aber: wenn du mehrere Datentypen in einer Liste haben willst. Dann kommst du nur mit compilezeit möglichkeiten nicht drumherum das du "raten" musst welchen Typ das Element hat. (Genau deswegen ist es nach meiner Ansicht keine gute Datenstruktur.)

In C# haste noch die laufzeitmöglichkeit der reflection. Was das C++ äquivalent ist weiss ich nicht. Und in einer simplen Liste mit reflection zu arbeiten ist nicht so pralle.

Falls das was du vorhast nicht rein zur übung ist und eventuell für etwas benutzt werden soll. Dann würde ich eher dazu raten deine Datenstruktur zu überdenken.

Woher ich das weiß:Studium / Ausbildung – Bachelor

Destranix  13.11.2022, 16:47
So haste die Liste und ihre Datentypen schonmal getrennt.

Gleichzeitig aber auch mehr Indirektion.

0
FouLou  13.11.2022, 17:16
@Destranix

Kannst du mir das in diesem Kontext Mal erklären? Ggf. An einem Beispiel?

0
Destranix  13.11.2022, 17:26
@FouLou

Nennt sich "intrusive list". Lässt sich denke ich besser selbst nachlesen. Im Prinzip kann der Compiler mehr tun und manche Operationen sind einfacher.
Ich finde gerade leider den Artikel dazu nicht mehr.

0