2009-10-27 8 views
9

私は自分自身にC++を教えようとしています。私が常に使ってきた伝統的な「新しい言語」演習の1つは、バイナリツリーやリンクリストのようなデータ構造を実装することです。 Javaでは、これは比較的簡単でした。インスタンス変数Object dataを保持していたクラスNodeを定義して、リストやツリーのすべてのノードにオブジェクトを格納できるようにしました。 (後で私はジェネリックスを使ってこれを修正することに取り組みました;それはこの質問に関するものではありません)。C++で任意のオブジェクト型への参照を保持しますか?

「あらゆるタイプのオブジェクト」を格納するのと同様の、慣用的なC++の方法はありません。 Cでは、voidポインタを使用します。同じことがC++にも当てはまりますが、std::stringのインスタンスを作成し、それをリスト/ツリーに格納しようとすると問題に遭遇します(std::string&からvoid*への無効なキャストに関するもの)。そんなやり方はありますか? C++にはJavaのObject(またはObjective-CのNSObject)と同等のものがありますか?

ボーナスの質問:そうではなく、ボイドポインタを使用し続ける必要がありますが、std::stringvoid*に保存する「正しい」方法は何ですか?私はstatic_cast<char*>(str.c_str())を見つけましたが、それは私がやろうとしていることのための冗長なようです。より良い方法がありますか?

+3

この質問はなぜ却下されたのですか? –

+0

私は知っていることにも興味があります - 私は二重引用符または何かを見つけることができませんでした... – Tim

+2

これは古い冗談のようです。患者:これをすると痛い。医者:それをしないでください。 C++では、「何か」のコレクションをしないでください。 –

答えて

11

Javaとは異なり、C++にはすべてのオブジェクトが継承する基本オブジェクトがありません。あなたがしたいことに対する通常のアプローチはtemplatesです。標準的なC++ライブラリのすべてのコンテナは、このアプローチを使用します。

Javaとは異なり、C++はジェネリックコンテナを実装するために多態性/継承に依存しません。 Javaでは、すべてのオブジェクトがObjectから継承されているため、いずれのクラスもObjectのコンテナに挿入できます。しかし、C++のテンプレートはコンパイラが実際に使用するそれぞれの型に対して異なるクラスを生成するようにコンパイラに指示するコンパイル時の構造体です。したがって、たとえば、あなたが持っている場合:

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

その後、std::stringオブジェクトを取るMyContainer作成し、int秒かかり、別のMyContainerことができます。

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

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

Javaジェネリックにテンプレートを精神的に類推するのは安全ですか? – Tim

+2

本当にありません。たとえ構文が表面的に同じであっても、それらは全く異なっています。 –

+3

詳細については、Javaジェネリックは継承と多型に依存する実行時のメカニズムです。 C++テンプレートは、各パラメータ化された型のコードを生成するようにコンパイラに指示するコンパイル時の構造体です。詳細については私の編集された答えを見てください。 –

3

あなたが探しているものはテンプレートです。これらのクラスを使用すると、クラスや関数を作成して、あらゆるデータ型を取ることができます。

1

void*を標準Cスタイルのキャストを使用してstring*にキャストできます。参照は、使用時にはポインタのように扱われず、通常のオブジェクトのように扱われることに注意してください。したがって、関数を参照することによって値を渡している場合は、そのアドレスを取得するためには、それを逆参照する必要があります。

他の人が言っているようしかし、これを行うには良い方法は、テンプレート

+1

Cスタイルのキャストは、C++では推奨されません。 –

2

テンプレートであるが、これを行うには、静的な方法です。それらはJavaやC#のジェネリックスのように動作しますが、100%スタティック(コンパイル時)です。同じコンテナにさまざまなタイプのオブジェクトを格納する必要がある場合は、これを使用します(他の回答はこれを非常によく説明しています)。

しかし、異なる種類のオブジェクトを同じコンテナに格納する必要がある場合は、にポインタを格納することで動的に行うことができます。

#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; 
} 

をスマートポインタを使用する方が簡単です:もちろん、あなたがそのような「オブジェクト」クラスはC++に存在しないため、独自のオブジェクト階層を定義する必要があります。 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

あなたは、任意のクラス::ブーストを見てみることができます。タイプセーフです。標準コレクションに入れることができ、ライブラリとリンクする必要はありません。クラスはヘッダーファイルで実装されています。

それはあなたがこのようなコードを記述することができます:

#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)); 
} 

完全なドキュメントはこちらです:http://www.boost.org/doc/libs/1_40_0/doc/html/any.html

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

は、私には奇妙に見えます。 str.c_str()はCのような文字列を取得しますが、タイプはconst char *で、char *に変換するには通常const_cast<char *>(str.c_str())を使用します。あなたがstringの内部と干渉しているので、それは良いことではないことを除いて。あなたはそれについて警告を受けていないと確信していますか?

static_cast<void *>(&str)を使用することができます。あなたが持っているエラーメッセージは、あなたが何か間違っていることを示唆しているので、コードを投稿すればそれを見ることができます。 (std::string&のデータ型は、stringへの参照で、ポインタではなく、エラーメッセージは正しいです。ポインタの代わりに参照を取得した方法がわかりません)

そして、はいこれは冗長です。それは意図されています。キャストは通常​​C++プログラムでは悪い匂いとみなされ、Stroustrupはキャストを簡単に見つけたいと考えました。他の答えで説明したように、任意の基本型のデータ構造を構築する正しい方法は、キャストやポインタではなくテンプレートを使用することです。

関連する問題