2009-10-27 4 views
9

Ich versuche, mir C++ beizubringen, und eine der traditionellen "neuen Sprachübungen", die ich immer benutzt habe, ist die Implementierung einiger Datenstrukturen, wie ein Binärbaum oder eine verkettete Liste. In Java war dies relativ einfach: Ich könnte einen Klassenknoten definieren, der eine Instanzvariable Object data verwaltet, so dass jemand jedes Objekt in jedem Knoten der Liste oder Struktur speichern könnte. (Später habe ich daran gearbeitet, dies mit Generika zu modifizieren; darum geht es bei dieser Frage nicht.)Behalten Sie Bezug auf irgendeinen Objekttyp in C++?

Ich kann keine ähnliche, idiomatische C++ - Methode finden, um "irgendeine Art von Objekt" zu speichern. In C würde ich einen void Zeiger verwenden; Das gleiche funktioniert offensichtlich für C++, aber dann stoße ich auf Probleme, wenn ich eine Instanz von std::string konstruiere und versuche, sie in der Liste/Struktur zu speichern (etwas über eine ungültige Umwandlung von std::string& bis void*). Gibt es so einen Weg? Hat C++ eine Entsprechung zu Java Object (oder Objective-Cs NSObject)?

Bonus Frage: Wenn es nicht, und ich muss weiter mit void-Zeigern, was ist die "richtige" Möglichkeit, eine std::string in eine void* zu speichern? Ich stolperte über static_cast<char*>(str.c_str()), aber das scheint irgendwie ausführlich für das, was ich versuche zu tun. Gibt es einen besseren Weg?

+3

Warum wurde diese Frage abgelehnt? –

+0

Ich bin auch daran interessiert zu wissen - ich konnte keinen Betrogenen oder so etwas finden ... – Tim

+2

Das ist wie der alte Witz. Patient: Es tut weh, wenn ich das tue. Doktor: Mach das nicht. Machen Sie in C++ keine Sammlung von "irgendetwas". –

Antwort

11

C++ hat kein Basisobjekt, von dem alle Objekte im Gegensatz zu Java erben. Der übliche Ansatz für das, was Sie tun möchten, wäre templates zu verwenden. Alle Container in der C++ - Standardbibliothek verwenden diesen Ansatz.

Im Gegensatz zu Java verlässt sich C++ nicht auf Polymorphie/Vererbung, um generische Container zu implementieren. In Java erben alle Objekte von Object, und daher kann jede Klasse in einen Container eingefügt werden, der eine Object übernimmt. C++ - Vorlagen sind jedoch Kompilierzeitkonstrukte, die den Compiler anweisen, für jeden von Ihnen verwendeten Typ tatsächlich eine andere Klasse zu generieren. So zum Beispiel, wenn Sie:

template <typename T> 
class MyContainer { ... }; 

Sie können dann erstellen MyContainer die std::string Objekte nimmt, und eine andere, die MyContainer int s nimmt.

MyContainer<std::string> stringContainer; 
stringContainer.insert("Blah"); 

MyContainer<int> intContainer; 
intContainer.insert(3342); 
+0

Ist es für mich sicher, Templates mit Java-Generika zu vergleichen? – Tim

+2

Nicht wirklich. Sie sind sehr unterschiedlich, auch wenn die Syntax oberflächlich gleich ist. –

+3

Java-Generics sind ein Laufzeitmechanismus, der auf Vererbung und Polymorphie beruht. C++ - Vorlagen sind Kompilierzeitkonstrukte, die den Compiler anweisen, Code für jeden parametrisierten Typ zu generieren. Siehe meine bearbeitete Antwort für weitere Informationen. –

3

Was Sie suchen, sind Vorlagen. Sie ermöglichen Ihnen, Klassen und Funktionen zu erstellen, mit denen Sie jeden beliebigen Datentyp verwenden können.

1

Sie sollten einen void* in einen string* unter Verwendung von C-Standard-Gussformen eingießen können. Denken Sie daran, dass eine Referenz bei der Verwendung nicht wie ein Zeiger behandelt wird, sondern wie ein normales Objekt behandelt wird. Wenn Sie also einen Wert durch Verweis auf eine Funktion übergeben, müssen Sie ihn noch abweisen, um seine Adresse zu erhalten.

jedoch, wie andere gesagt haben, ein besserer Weg, dies zu tun ist mit Vorlagen

+1

C-Style-Casts sollten in C++ entmutigt werden. –

2

Vorlagen die statische Art und Weise, dies zu tun. Sie verhalten sich wie Java und C# Generika, sind aber zu 100% statisch (Kompilierzeit). Wenn Sie verschiedene Arten von Objekten im selben Container speichern möchten, verwenden Sie dies (andere Antworten beschreiben dies sehr gut).

Wenn Sie jedoch verschiedene Arten von Objekten in demselben Container speichern müssen, können Sie dies auf dynamische Weise tun, indem Sie Zeiger auf einer Basisklasse speichern.Natürlich müssen Sie Ihre eigenen Objekte Hierarchie definieren, da es keine solche „Objekt“ Klasse in C++ ist:

#include <list> 

class Animal { 
public: 
    virtual ~Animal() {} 
}; 

class Dog : public Animal { 
public: 
    virtual ~Dog() {} 
}; 

class Cat : public Animal { 
public: 
    virtual ~Cat() {} 
}; 

int main() { 
    std::list<Animal*> l; 
    l.push_back(new Dog); 
    l.push_back(new Cat); 

    for (std::list<Animal*>::iterator i = l.begin(); i!= l.end(); ++i) 
     delete *i; 

    l.clear(); 

    return 0; 
} 

Ein Smart-Pointer ist einfacher zu bedienen. Beispiel mit boost::smart_ptr:

std::list< boost::smart_ptr<Animal> > List; 
List.push_back(boost::smart_ptr<Animal>(new Dog)); 
List.push_back(boost::smart_ptr<Animal>(new Cat)); 
List.clear(); // automatically call delete on each stored pointer 
9

Sie können einen Blick auf boost :: nehmen jede Klasse. Es ist typsicher, Sie können es in Standard-Collections einfügen und Sie müssen nicht mit einer Bibliothek verknüpfen, die Klasse ist in der Header-Datei implementiert.

Es ermöglicht Ihnen, Code wie folgt zu schreiben:

#include <list> 
#include <boost/any.hpp> 

typedef std::list<boost::any> collection_type; 

void foo() 
{ 
    collection_type coll; 
    coll.push_back(boost::any(10)); 
    coll.push_back(boost::any("test")); 
    coll.push_back(boost::any(1.1)); 
} 

Die vollständige Dokumentation ist hier: http://www.boost.org/doc/libs/1_40_0/doc/html/any.html

1
static_cast<char*>(str.c_str()) 

scheint mir seltsam. str.c_str() ruft die C-ähnliche Zeichenfolge ab, aber mit dem Typ const char *, und um zu char * zu konvertieren, würden Sie normalerweise const_cast<char *>(str.c_str()) verwenden. Abgesehen davon, dass das nicht gut ist, da Sie sich mit dem Inneren eines string einmischen würden. Bist du sicher, dass du keine Warnung bekommen hast?

Sie sollten static_cast<void *>(&str) verwenden können. Die Fehlermeldung, die Sie erhalten haben, weist darauf hin, dass Sie etwas anderes falsch verstanden haben. Wenn Sie also den Code posten könnten, könnten wir uns das ansehen. (Der Datentyp std::string& ist eine Referenz auf eine string, kein Zeiger auf einen, so dass die Fehlermeldung richtig ist. Was ich nicht weiß, ist, wie Sie eine Referenz anstelle eines Zeigers erhalten.)

Und ja , das ist ausführlich. Es soll so sein. Casting wird in C++ in der Regel als schlechter Geruch angesehen und Stroustrup wollte, dass Casts leicht zu finden sind. Wie in anderen Antworten diskutiert wurde, ist der richtige Weg, um eine Datenstruktur beliebigen Basistyps zu erstellen, die Verwendung von Templates, nicht von Umwandlern und Zeigern.