2014-10-06 2 views
7

Ich versuche, einen Vektor in C++ zu machen, der 3 verschiedene Datentypen speichern kann. Ich möchte die Boost-Bibliothek nicht verwenden. Etwas wie:Vektor, der 3 verschiedene Datentypen haben kann C++

vector<type1, type2, type3> vectorName; 

Benötige ich eine Vorlage zu machen? Und wenn ja, wie würde ich das tun?

+0

Sie benötigen wahrscheinlich eine Art Wrapper-Klasse/Container, die Verweise auf die verschiedenen Typen enthält. Eine ähnliche Frage [hier] (http://stackoverflow.com/questions/13461869/c-push-multiple-types-onto-vector) – Abbath

+0

Siehe ['tuple'] (http://www.cplusplus.com/reference/tuple /), vielleicht. Vektoren (auch Listen genannt) sind Sequenzen von einem oder mehreren * homogenen * Items. Tupel sind endliche (und feste Größe) Sammlungen von möglichen heterogenen Mitgliedern. – user2864740

+2

Möchten Sie einen Vektor haben, dessen Elemente alle drei Typen enthalten? Oder ein Vektor, der einen Typ oder die anderen enthalten kann? – Galik

Antwort

6

Die einfachste Möglichkeit, mehrere Typen in einem Vektor zu speichern, besteht darin, sie zu Subtypen einer übergeordneten Klasse zu machen und die gewünschten Typen in Klassen zu verpacken, wenn sie noch keine Klassen sind.

class Parent 
{ 
    //anything common to the types should be declared here, for instance: 
    void print() //make this virtual if you want subclasses to override it 
    { 
    std::cout << "Printing!"; 
    } 

    virtual ~Parent(); //virtual destructor to ensure our subclasses are correctly deallocated 
}; 

class Type1 : public Parent 
{ 
    void type1method(); 
}; 

class Type2 : public Parent 
{ 
    void type2Method(); 
}; 


class Type3 : public Parent 
{ 
    void type3Method(); 
}; 

Sie dann einen Vektor von Parent Zeiger erstellen können, die Zeiger auf die Kind-Typen speichern kann:

std::vector<Parent*> vec; 

vec.push_back(new Type1); 
vec.push_back(new Type2); 
vec.push_back(new Type3); 

Wenn Elemente direkt aus dem Vektor zugreifen können, können Sie nur in der Lage sein, Mitglieder zu verwenden, die gehören zu Parent. Zum Beispiel können Sie schreiben:

vec[0]->print(); 

aber nicht:

vec[0]->type1Method(); 

als Elementtyp hat als Parent* und der Parent Typ keine type1Method erklärt wurde.

Wenn Sie die Subtyp-spezifischen Mitglieder zugreifen müssen, können Sie die Parent Zeiger konvertieren Zeiger auf Subtyp wie so:

Parent *p = vec[0]; 

Type1 *t1 = nullptr; 
Type2 *t2 = nullptr; 
Type3 *t3 = nullptr; 

if (t1 = dynamic_cast<Type1*>(p)) 
{ 
    t1->type1Method(); 
} 
else if (t2 = dynamic_cast<Type2*>(p)) 
{ 
    t2->type2Method(); 
} 
else if (t3 = dynamic_cast<Type3*>(p)) 
{ 
    t3->type3Method(); 
} 

Obwohl es in der Regel eine bessere Idee zu vermeiden, diese Art von expliziten Typ-Verzweigung betrachtet hat und sich stattdessen auf virtuelle Methoden verlassen.

Achten Sie darauf, die Zeiger zu löschen, bevor Sie sie aus dem Vektor entfernen, wenn Sie die dynamische Zuordnung verwenden, wie im obigen Beispiel. Alternativ können Sie intelligente Zeiger (wahrscheinlich std::unique_ptr und lassen Sie Ihre Erinnerung an sich selbst kümmern:

std::vector<std::unique_ptr<Parent>> vec; 
+0

Sie haben nichts gelöscht. Warum nicht einen Vektor von unique_ptr verwenden, damit er automatisch gelöscht wird? Parent benötigt einen virtuellen Destruktor. –

+0

@NeilKirk Richtig, vergaß den virtuellen Destruktor. Ich werde auch intelligente Zeiger erwähnen. – ApproachingDarknessFish

+0

Ok Ich kann meinem Vektor verschiedene Typen hinzufügen, aber ich kann nicht auf die spezifischen Methoden in den Typen zugreifen. Wie kann ich auf diese verschiedenen Methoden zugreifen? Zum Beispiel, wenn ich vec [i] .print() aufrufen möchte; Danke für die Hilfe! –

5

Ich versuche, einen Vektor in C++ zu machen, die 3 verschiedene Datentypen speichern können.

Die Antwort hier wirklich auf dem speziellen Anwendungsfall abhängt:

  1. Wenn die Objekte irgendwie in irgendeiner Art und Weise und ähnlichen verbunden - schafft eine Basisklasse und leitet alle Klassen von ihm, dann machen der Vektorspeicher unique_ptr s zu der übergeordneten Klasse (siehe ApproachingDarknessFish s Antwort für Details),

  2. Wenn die Objekte alle grundlegenden (so eingebaut) Typen sind - verwenden, um eine union dass Gruppen eine vector<yourUnionType>,

  3. Wenn die Objekte unbekannter Art, aber Sie sind sicher, dass sie teilen ein ähnliches Interface, erstellen Sie eine Basisklasse, und leiten eine Templat-Kind-Klasse von ihm (template <typename T> class container: public parent{};), definieren die Art und und erstellen sie eine vector<unique_ptr<parent>> wie im ersten Fall,

  4. Wenn die Objekte von Typen, die aus irgendeinem Grund nicht (so zum Beispiel die vector speichert int, std::string und yourType) angeschlossen werden, verbinden sie über einen union, wie in . Oder - noch besser ...

... wenn Sie Zeit haben und wollen etwas lernen - aussehen wie boost::any implementiert ist und versuchen Sie es selbst Implementierung, wenn Sie wirklich die Bibliothek nicht verwenden möchten selbst . Es ist nicht so schwer wie es scheinen mag.

+0

Warum die Einschränkung in Option 2? – MSalters

+0

@MSalters, weil ich fest davon überzeugt bin, dass, wenn sie nicht - Ableitung einer Basisklasse ist eine objektorientierte Lösung. Auch - die Verwendung von Unionen in einem "Vektor" ist nicht besonders praktisch - Sie müssen zusätzlich einen sekundären Vektor speichern, der den Typ enthält, den jede Vereinigung speichert, damit Sie richtig darauf zugreifen und keinen Müll bekommen. –

+0

Die Notwendigkeit für einen Vektor von Diskriminanten wird auch für grundlegende Typen benötigt, so dass es keinen Grund gibt, sie zu unterscheiden. Und wie Sie bemerken, ist das Fehlen einer gemeinsamen Basisklasse ein echtes Problem, also warum nicht bei Option 2 in diesem Fall anstelle des hässlichen Hacks in 4 bleiben? – MSalters