How to create copy safe container with std :: list iterators stored in std vector in C ++?

For my GUI, I need a class with the following control objectives for controls (windows, buttons, etc.)

  • random access to elements using [index]
  • random access to elements using ["key"]
  • pointer stability, so ptr=&container[index] will not change if elements are added or deleted
  • copy protection. All items must be stored in the container and copied if '=' is used as container2=conatiner1 (deep copy)
  • the order of the items in the list should be volatile, but pointers to the items should remain valid. If ptr1=container[1] and ptr2=container[2] , then after replacing orders 1 and 2 ptr1==container[2] and ptr2==container[1]

I came to the conclusion that std :: list provides the stability of the pointers I need and std :: vector random access. So I have an idea to save a tuple of std string and iterator in a vector. However, after copying the container, all iterators are invalid.

Any suggestions on how best to solve this problem?

Here's the main code for the current approach (only important parts are included):

 template < class T > class ControlList { struct Tuple{std::string first;typename std::list<T>::iterator second;}; std::vector<Tuple> list; std::list<T> objects; inline T& operator [](int i) { return *list[i].second; } inline T& operator [](std::string s) { loopi(0,vlist.size()) if(s==vlist[i].first) return *vlist[i].second; } } 

Access to the lines is slow, but usually the container contains no more than 10 elements and is rarely used in the program.

Update:

The generic pointer is already good, but it cannot solve for the deep copy that I need. Suppose I have window2 = window1. Now, if I have a common pointer, then pressing a button in window2 also pushes the same button in window1, which is not required. I really need a new instance of all the objects contained in the container.

Can I override the copy constructor to create new instances of objects referenced by smart pointers?

Both windows and buttons are stored in the ControlList , where the window contains several lists.

Update2:

Overriding the copy constructor and assignment constructor seems to have solved the problem

Update3:

I just released a GUI for which this class was intended for MIT.

Download here.

+5
source share
1 answer

If you were to use std::vector<std::pair<std::string, std::unique_ptr<T>>> , you could copy the elements, but you would like, and for the result you need only one indirect access step . This will eliminate most of the complexity that you now have with three different structures. As a bonus, items will also automatically clear after themselves.

If you need semantics of an owner-observer with pointers, you can instead choose std::shared_ptr<T> and std::weak_ptr<T> . Generic pointers can easily create weak pointers that act as independent observers that do not affect the calculation of a generic pointer.

Edit: Just add, shared_ptr , and the other smart pointers are C ++ 11 and later exlcusive. If you want to use C ++ 03 compatible solutions, you can look at past Boost implementations or, perhaps, create them yourself, observing the C ++ 11/14 specification.

Edit2: The following is the code:

http://coliru.stacked-crooked.com/a/a9bf52e5428a48af

 #include <vector> //vector #include <memory> //smart pointers #include <utility> //pair #include <string> //string #include <iostream>//cout template <class T> class Container { public: inline void push(const std::string& s, const T& t) { objects.push_back(std::pair<std::string, std::shared_ptr<T>>(s, std::make_shared<T>(t))); } inline T& operator [](const size_t& i) { return *(objects[i]->second); } inline T& operator [](const std::string& s) { for (auto it : objects) { if(s == it.first) { return *(it.second); } } //welp, what do you do here if you can't find it? } private: std::vector<std::pair<std::string, std::shared_ptr<T>>> objects; }; int main() { Container<int> cont; std::string str {"hi"}; int i {2}; cont.push(str, i); //This is good... std::cout << cont["hi"] << std::endl; //But undefined behavior! std::cout << cont["02"] << std::endl; return 0; } 
+3
source

All Articles