2011-01-27 1 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 обычно предпочтительны между ними).

В качестве альтернативы вместо того, чтобы возвращать указатель (как правило, это плохая идея), вы можете вернуть индекс строки и предоставить функцию-член, которая индексируется в вектор, когда он используется.

+0

Это хорошая идея с номером индекса, но не работает для большей проблемы, для которой это упрощение. Я использую решение std :: list вашего и peoro. – 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 класса в Boost, целью которого является сделать такое решение более многоразовым и надежным (т. Е. Автоматически управляет памятью, поэтому вам не нужно беспокоиться об удалении строки при удалении ее указателя из вектора и т. Д.).

+0

Спасибо. Но я пойду с решением Перо, в котором перечислены списки. Это сложный способ сделать это. Это хороший ответ, если он работает, хотя он сохраняет как использование векторов, так и интерфейсов магазина. – 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

Смежные вопросы