2011-01-27 8 views
2

以下のプログラムでは、空のストアに文字列が追加されます。次に、このストア要素のアドレスがポインタ 's1'に格納されます。次に別の文字列が追加され、元の要素へのポインタが何らかの理由で失敗します。文字列とストア

#include <iostream> 
#include <string> 
#include <vector> 

class store2 
{ 
    public: 
     void add(std::string s) {words.push_back(s); last_added2 = &words.at(words.size() - 1);} 
     std::string* last_added() {return last_added2;} 

    private: 
     std::string* last_added2; 
     std::vector<std::string> words; 
}; 

void main() 
{ 
    store2 store; 
    store.add("one"); 
    std::string* s1 = store.last_added(); 
    std::cout<<*s1<<std::endl; 
    store.add("two"); 
    std::cout<<*s1<<std::endl; // crash 
} 

答えて

3

あなたはstd::vectorに新しい項目を追加すると、ベクトルはそのバッファを拡大する必要がありますし、これを行うことによって、それはおそらく別のメモリ領域にバッファを移動します。したがって、その要素へのポインタは無効になります。それを短くするために、ベクトルの項目へのポインタは、ベクトルのサイズ変更後に有効であるとは保証されておらず、push_backは、十分な予約領域がない場合、ベクトルのサイズを変更することがあります。

最初にベクターのためのスペースを予約できますが、ベクターに割り当てることのできるアイテムの数に制限があります。

+0

これはstd :: vectorである必要はなく、std :: vectorsの多くの能力は必要ありませんが、サイズを変更する必要があり、要素は確実にアドレス可能である必要があります。 – alan2here

+2

ランダムアクセスが必要ない場合は、 'std :: list'を使うことができます – peoro

+0

最近追加されたアイテムをリストにアクセスして個々の要素にアドレスを保持することは可能ですか? – alan2here

1

あなたは、コレクションへのポインタが有効であることを保証する必要がある場合、あなたはおそらく(例えば、あなたが代わりにstd::dequeまたはstd::listを使用することができます - std::dequeは、一般的に2つの間で好まれていると)、ベクター以外の何かをしたいです。

また、ポインタ(通常はとにかく問題)を返す代わりに、文字列のインデックスを返し、使用時にインデックスを付けるメンバ関数を提供することができます。

+0

インデックス番号は良いアイデアですが、これが単純化されている大きな問題には取り組まずにいます。私はあなたとpeoのstd :: listソリューションを使用しています。 – alan2here

0

std::vectorの内容が変更されると、イテレータを無効にすることができます。 vector iterator invalidationを参照してください。あなたは本当に既存のインターフェースを維持し、あなたのベクトルに挿入された要素からポインタを保持したい場合は

、あなたは例えば、ポインタではなく値で文字列を格納することができます

#include <iostream> 
#include <string> 
#include <vector> 
#include <memory> 

class store2 
{ 
public: 
    store2() 
    { 
    } 

    ~store2() 
    { 
     for (std::vector<std::string *>::iterator it = 
       words.begin(), end_it = words.end(); 
      it != end_it; ++it) 
     { 
      delete *it; 
     } 
     words.clear(); 
    } 

    void add (const std::string & s) 
    { 
     std::auto_ptr<std::string> v (new std::string (s)); 
     words.push_back (v.get()); 
     v.release(); 
    } 

    std::string *last_added() 
    { 
     return words.back(); 
    } 

    const std::string *last_added() const 
    { 
     return words.back(); 
    } 

private: 
    std::vector<std::string *> words; 
}; 

int main() 
{ 
    store2 store; 
    store.add("one"); 
    std::string* s1 = store.last_added(); 
    std::cout<<*s1<<std::endl; 
    store.add("two"); 
    std::cout<<*s1<<std::endl; // no crash :-) 
} 

はにもptr_vectorクラスがありますこの種のソリューションをより再利用可能かつ堅牢なものにすることを目指すブースト(例えば、自動的にメモリを管理するので、ベクトルからポインタを消去するときに文字列を削除する心配はありません)。

+0

ありがとうございます。しかし、私はリストを含むピオロの解決策と一緒に行きます。これは、それを行うverry複雑な方法です。それはベクトルの使用とストアのインターフェースの両方を保存するので、それがうまくいくなら、良い答えです。 – alan2here

1

ポインタ(ヒープ)を使用する特別な理由はありますか? ない場合は、単に実行します。

class store2 
    { 
     public: 
      void add(std::string s) {words.push_back(s);} 
      std::string last_added() { if (words.size() == 0) return ""; 
return words[words.size()-1];} 

     private: 
      std::vector<std::string> words; 
    } 

+0

+1無効になる可能性のある一時ファイルを保存しないで明白な方法です(代わりに 'words.back()'を使います)。 –

+0

要素のコピーではなく要素へのポインタを返す必要があります。 – alan2here

関連する問題