2012-11-07 17 views
5

Dla takiego przypadku:Właściwa inicjalizacji inteligentne kursory tablicy

class A 
    { 
     //implementation 
    }; 

    class B 
    { 
    public: 
     B(); 
     ~B(); 
    private: 
     std::vector<std::shared_ptr<A>> _innerArray; 
    }; 

co należy zrobić w B() utworzyć obiekt z obowiązującym stanie? Czy muszę ręcznie wywoływać domyślny konstruktor dla każdego obiektu A w tablicy? I czy muszę zrobić coś specjalnego w ~B()? Jeśli klasa B jest przykładem złego projektu, możesz powiedzieć, jak ją ulepszyć. Dzięki.

Edytuj Oto schemat tego, czego naprawdę potrzebuję.

enter image description here

tak rzeczywiste wartości przechowywane tylko w tablicy A i wszystkie inne obiekty służą do przechowywania połączeń. Najprostszy przykład - A = kropka, B = linia (lub krzywa) przechodząca przez wybrane kropki i C = płaszczyzna opisana liniami. Mam nadzieję, że czyni to pytanie dokładniejszym.

+1

Kontrola poczytalności: czy w ogóle potrzebujesz wskazówki? A jeśli tak, czy powinien to być wspólny wskaźnik? –

+0

Klasa B powinna mieć dostęp do niektórych obiektów przechowywanych gdzie indziej, ale nie do nich należeć. Nie widzę używania unique_ptr zamiast shared_ptr w sensie tutaj, ale jestem noob do inteligentnych wskaźników. –

+0

@Pavel Masz rację: Twój scenariusz wyklucza użycie 'unique_ptr' i nakazuje użycie wskaźników. Jednakże, jeśli 'B' nie ma przejąć własności, rozważałbym użycie surowych wskaźników zamiast wspólnych wskaźników. W końcu te ostatnie oznaczają * własność współdzieloną * (ale być może tego właśnie potrzebujesz). –

Odpowiedz

4

Aby utworzyć obiekt B w prawidłowym stanie, nie trzeba nic więcej robić. Nawet nie musisz deklarować i implementować konstruktora i destruktora dla B. std::vector<std::shared_ptr<A>>, który jest członkiem B zostanie domyślnie zainicjowany w konstruktorze B, co oznacza, że ​​nie ma jeszcze żadnych elementów w kontenerze. Zostanie on również poprawnie usunięty w ~B dzięki destruktorom std::vector i.

Z drugiej strony, jeśli chcesz np. Zainicjować go w jakiś sposób (tj.3 wartości) można użyć konstruktora std::vector 's na liście inicjalizującej konstruktora B. Na przykład:

class B 
{ 
public: 
    B(): _innerArray{ std::make_shared<A>(), 
         std::make_shared<A>(), 
         std::make_shared<A>() } {} 
    ~B() {} 
private: 
    std::vector<std::shared_ptr<A>> _innerArray; 
}; 

Pamiętaj, że std::make_shared wykorzystuje doskonałe przekazywanie więc zdać A argumenty „s konstruktora jako argumentów funkcji, a nie sam obiekt klasy.

Odpowiadając na twoje obawy dotyczące projektu, chciałabym Cię zachęcić, aby najpierw pomyślał o wyłącznej własności członków w wektorze, zanim zdecydujesz się je udostępnić.

class B 
{ 
public: 
    B(); 
    ~B(); 
private: 
    std::vector<std::unique_ptr<A>> _innerArray; 
}; 

Powyższa implementacja jest bardziej skuteczna na wielu podstawach. Przede wszystkim sprawia, że ​​Twój projekt jest bardziej przejrzysty w kwestii tego, kto jest odpowiedzialny za całe życie A. Następna std::unique_ptr jest szybsza, ponieważ nie wymaga liczenia odwołań do wątków. I na koniec, nie kosztuje żadnej dodatkowej pamięci (w porównaniu do zwykłego wskaźnika C), podczas gdy std::shared_ptr może zająć dziesiątki bajtów (24-48), aby przechowywać dane stanu współdzielonego, co jest wysoce nieskuteczne, gdy operujesz na małych klasach. Dlatego zawsze używam std::unique_ptr jako mojego pierwszego inteligentnego wskaźnika pointeru, a ja tylko cofam się do std::shared_ptr, gdy jest to naprawdę potrzebne.

EDIT:

Odpowiadając edycję chciałbym stworzyć 3 pojemniki klas A, B, C. W zależności od tego, czy trzeba im być polimorficzny lub nie będzie przechowywać zarówno wartości tak (typu non-polimorficzne):

std::deque<A> as; 
std::deque<B> bs; 
std::deque<C> cs; 

lub (typy polimorficzne):

std::vector<std::unique_ptr<A>> as; 
std::vector<std::unique_ptr<B>> bs; 
std::vector<std::unique_ptr<C>> cs; 

w tej kolejności (as musi żyć dłużej niż bs i bs musi żyć dłużej niż cs). Wtedy po prostu miałbym std::vector<A*> wewnątrz klasy B i std::vector<B*> wewnątrz klasy C bez użycia inteligentnych wskaźników.

Mam nadzieję, że to pomaga.

EDIT:

Zmieniono std::vector do std::deque w pierwszym przypadku, który pozwala referencje/wskaźniki do elementów kontenerowych przetrwać kontenery rozszerzeń z push_back(). Jednak nie przetrwają wymazania elementów, sortowania lub innych rzeczy.

+0

Świetna odpowiedź. Idealny. –

2

Jeśli zrobisz to w ten sposób, wektor ma rozmiar elementów zerowych, tzn. Zawartość jest banalnie poprawnie zainicjowana. Jeśli wektor miał dodatni rozmiar (na przykład po wywołaniu resize na wektorze), każdy z elementów byłby prawidłowo inicjowany. Ponieważ elementy te są shared_ptr s, domyślny konstruktor shared_ptr byłby wywoływany, co oznacza, że ​​skończyłby się wektor pustych wskaźników.

Jeśli chcesz skopiować zawartość z innego pojemnika, użyj wersji iteracyjnej konstruktora Vector:

B (SomeContainerTypeContainingSharedPointers container) 
: _innerArray (container.begin(), container.end()) { 
} 

Jeśli nie chcesz zainicjować wektor z pojemnika, ale z innego miejsca (np twórz obiekty w locie) - wpisz typ iteratora wejściowego (np. rodzaj "iteratora fabrycznego").

1

Wektor jest pusty, więc nie trzeba wykonywać żadnych specjalnych czynności w domyślnym konstruktorze. I nie musisz nic robić w B(). Licznik referencji shared_ptrs zostanie automatycznie zmniejszony po wywołaniu destruktora wektora.

1

Domyślnie Bt std::shared_ptr<A> zapełni wewnętrzny plik Ptr za pomocą NULL. Aby stworzyć inteligentne wykorzystanie wskaźnika std::make_shared:

_innerArray.push_back(std::make_shared<A>(/*constructor params here*/)); 

Ale w przykładzie wektora jest pusty.

0

Domyślny konstruktor robi już wszystko, co jest potrzebne. Możesz nawet opuścić B() bez żadnych strat.