2015-07-14 15 views
5

Dla mojego GUI muszę klasę z następujących celów zarządzania kontrolnej (okien, przycisków itp)Jak utworzyć bezpieczny pojemnik do kopiowania za pomocą std :: list iteratorów przechowywanych w standardzie w C++?

  • swobodny dostęp do elementów przez [index]
  • losowego dostępu do elementów przez ["key"]
  • stabilność wskaźnika , więc ptr=&container[index] nic nie zmieni, jeśli elementy zostaną dodane lub usunięte
  • skopiować bezpieczeństwo. Wszystkie elementy muszą zostać zapisane w kontenerze i skopiowane, jeśli "=" jest używane tak jak container2=conatiner1 (deep-copy)
  • kolejność elementów na liście musi być możliwa do zmiany, ale wskaźniki do elementów muszą pozostać poprawne. Jeśli ptr1=container[1] i ptr2=container[2], to po zamianie rzędu 1 i 2, ptr1==container[2] i ptr2==container[1]

doszedłem do wniosku, że std :: Lista zapewnia stabilność dla wskaźników, które muszę i std :: vector losowego dostęp. Więc mam pomysł przechowywania krotki std string i iteratora w wektorze. Jednak iteratory są nieważne po skopiowaniu kontenera.

Jakieś sugestie, jak najlepiej rozwiązać ten problem?

Tutaj główny kod z obecnego podejścia (tylko ważne elementy są w zestawie):

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

Dostęp ciąg jest powolny, ale zazwyczaj pojemnik ma nie więcej niż 10 elementów i jego używane rzadko w programie .

Aktualizacja:

Wspólna wskaźnik jest już dobrze, ale nie da się rozwiązać za głębokiej kopii muszę. Powiedzmy, że mam window2 = window1. Teraz, jeśli mam wspólny wskaźnik, naciśnięcie przycisku w oknie 2 powoduje również naciśnięcie tego samego przycisku w oknie 1, które nie jest pożądane. Naprawdę potrzebuję nowej instancji wszystkich obiektów zawartych w kontenerze.

Czy możliwe jest zastąpienie konstruktora kopii w celu utworzenia nowych wystąpień obiektów przywoływanych przez inteligentne wskaźniki?

Zarówno okna i przyciski są przechowywane w pliku ControlList, w którym okno zawiera wiele list.

Update2:

Zastępowanie konstruktora kopii i konstruktora przypisania najwyraźniej rozwiązać problem

Update3:

Właśnie wydany GUI klasa ta została przeznaczona na licencji MIT.

Download here.

+0

Czy ten C++ C++ 11 lub 03? – VermillionAzure

+0

Powinieneś mieć jeden kontener z modelowaniem najbardziej ogólnych wymagań (to jest std :: vector do obsługi elementów GUI) i widoków do tego kontenera (std :: vector :: iterator) –

+0

Z ciekawości mówisz "Dostęp do łańcucha jest wolny", co jest wolne i jak ważna jest jego prędkość? (Powiedziałeś, że nie jest często używany, ale czy jest używany w krytycznych momentach czasu?) Może to potencjalnie wpłynąć na proponowane rozwiązanie. –

Odpowiedz

3

Jeśli było użyć std::vector<std::pair<std::string, std::unique_ptr<T>>>, można skopiować elementy jednak chciał, a otrzymana wartość będzie wymagać tylko jeden etap pośredni do dostępu.To wyeliminowałoby dużą część złożoności, jaką posiadasz teraz z 3 różnymi strukturami. Jako bonus, przedmioty będą również automatycznie czyścić po sobie.

Jeśli potrzebujesz semantyki właściciel-obserwator ze wskaźnikami, możesz zamiast tego wybrać opcję std::shared_ptr<T> i std::weak_ptr<T>. Współdzielone wskaźniki mogą z łatwością tworzyć słabe wskaźniki, które działają jako obserwatorzy nieposiadający posiadania, które nie wpływają na liczenie referencyjne współużytkowanego wskaźnika.

Edycja: Wystarczy dodać, shared_ptr, a pozostałe inteligentne wskaźniki to C++ 11 i później - exlcusive. Jeśli chcesz rozwiązań zgodnych z C++ 03, możesz sprawdzić wcześniejsze implementacje Boost lub samodzielnie je utworzyć, przestrzegając specyfikacji C++ 11/14.

Edit2: Oto kod, aby pomóc:

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

Dziękuję za szczegółową odpowiedź. Zaadoptowałem twój kod i zastąpiłem moją starą klasę std: vector , z struct Tuple {std :: string first, T second;}. Niestety, cały system okienny zawiódł później, a liczba klatek spadła do 1/10. Dodam szczegóły w pytaniu oryginalnym –

+0

Teraz działa :) Wygląda na to, że był to błąd po mojej stronie. Jeszcze raz! –

+0

@MarcoPolo Powinieneś zaakceptować odpowiedzi, które są poprawne ... – VermillionAzure

Powiązane problemy