开发者

strings and a store

In the below program a string is added to an empty store. Then the address of this store element is stored in the pointer 's1'. Then another string is added and this somehow causes the pointer to the original element to fail.

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

class store2
{
    public:
        void a开发者_JAVA技巧dd(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
}


When you add a new item to an std::vector, the vector might require to expand its buffer, and by doing this it will probably move the buffer in a different memory area. Thus pointers to its element become invalid. To make it short, pointers to the items of a vector are not guaranteed to be valid after resizing a vector, and push_back may resize the vector if it hasn't got enough reserved space.

You could reserve space for the vector at the beginning, but then you'll have a limit to the number of items you can allocate into your vector.


If you need to assure that pointers into the collection remain valid, you probably want something other than a vector (e.g., you could use a std::deque or std::list instead -- with std::deque generally being preferred between the two).

Alternatively, instead of returning a pointer (generally a poor idea anyway), you could return the index of the string, and provide a member function that indexes into the vector when it's used.


Do you have any particular reason you want to use pointers(heap)? If not, just do:

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

;


std::vector's iterators can be invalidated when its content is modified. See vector iterator invalidation.

If you really want to keep the existing interface and retain pointers from elements inserted to your vector, you can store string by pointers and not by value, for example:

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

There is also ptr_vector class in Boost that aims to make this kind of solution more reusable and robust (i.e. automatically manages memory, so you don't have to worry about deleting string when erasing its pointer from vector etc).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜