2011-01-27 58 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,向量可能需要擴大其緩衝器,並通過這樣它可能會傳送緩衝區在不同的存儲區。因此指向其元素的指針變得無效。爲了簡短起見,在矢量調整大小之後,指向矢量項的指針不能保證有效,並且如果矢量沒有足夠的保留空間,則可能調整矢量大小。

您可以在開始時爲矢量保留空間,但是您可以限制可以分配到矢量中的項目數量。

+0

它不需要是std :: vector,並且許多std :: vectors功能不是必需的,但它確實需要調整大小,並且元素確實需要可靠地尋址。 – alan2here 2011-01-27 15:49:10

+2

如果你不需要隨機訪問,你可以使用`std :: list` – peoro 2011-01-27 15:50:45

+0

是否可以訪問列表中最近添加的項目,並保留個別元素的地址? – alan2here 2011-01-27 15:52:32

1

如果你需要確保指針到集合中依然有效,你可能要超過一個載體以外的東西(例如,你可以使用std::dequestd::list替代 - 與std::deque一般將兩者之間的優先)。

或者,您可以不使用返回指針(通常是一個糟糕的主意),而是返回字符串的索引,並提供一個成員函數,該函數在使用時指向向量。

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類旨在使這種解決方案更具可重用性和強大性的升級(即自動管理內存,因此您不必擔心刪除字符串時刪除其指針等指針)。

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

;