2010-07-26 9 views
5

W C++ ff Mam klasy, które musi posiadać element, który może być dynamicznie przeznaczone i wykorzystane jako wskaźnik, czy nie, jak to:Dynamiczne vs non-dynamiczny członkowie klasy

class A { 
    type a; 
}; 

lub

class A { 
    A(); 
    ~A(); 
    type* a; 
}; 

i konstruktora:

A::A { 
    a = new type(); 
} 

i destruktora:

A::~A { 
    delete a; 
} 

Czy są jakieś zalety lub wady jednego z nich, oprócz dynamicznego wymagającego więcej kodu? Czy zachowują się inaczej (pomijając wskaźnik, który trzeba usunąć) lub czy są wolniejsze od innych? Którego powinienem użyć?

+2

Pracuj nad poprawnym, możliwym do utrzymania kodem, a następnie w razie potrzeby pracuj nad wydajnością. –

+1

Twoje drugie "A" jest niebezpieczne; musisz zdefiniować także konstruktora kopiowania i 'operator ='. Nigdy nie posiadaj takich wskaźników, owiń je tak, abyś nie musiał pisać * żadnych * specjalnych funkcji członków. – GManNickG

Odpowiedz

0

Dzięki wskaźnikowi masz większą kontrolę, ale także więcej obowiązków. Masz większą kontrolę w tym sensie, że możesz precyzyjniej decydować o długości życia obiektu, podczas gdy bez wskaźnika czas życia jest zasadniczo równy czasowi życia obiektu zawierającego. Ponadto, przy pomocy wskaźnika element mógłby faktycznie być instancją podklasy typu wskaźnika.

Z punktu widzenia wydajności użycie wskaźnika oznacza większe zużycie pamięci, większą fragmentację pamięci i dereferencję. Jednak dla wszystkich z wyjątkiem najbardziej krytycznych pod względem wydajności kodu, nie warto się tym martwić.

0

Główna różnica polega na tym, że wskaźnik może potencjalnie wskazywać gdzie indziej.

edit

odpowiedź Wawrzyńca nie jest źle, ale to trochę ogóle. W szczególności alokacja dynamiczna będzie nieco wolniejsza. Dereferencja przez wskaźnik również będzie nieznacznie wolniejsza. Ponownie, nie jest to duża strata prędkości, a elastyczność, którą kupuje, może być bardzo opłacalna.

0

Główna różnica polega na tym, że jeśli nie używasz wskaźnika, pamięć dla wewnętrznego elementu zostanie przydzielona jako część pamięci przydzielonej dla obiektu zawierającego. Jeśli użyjesz new, otrzymasz pamięć w oddzielnych porcjach (już wydaje się, że masz odpowiednie tworzenie i niszczenie odwoływanego obiektu w dół)

0

Musisz zrozumieć implikacje domyślnego konstruktora kopiowania i operatorów przypisania kopiowania, gdy używasz surowych wskaźników . Surowy wskaźnik zostaje skopiowany w obu przypadkach. Innymi słowy, skończy się posiadanie wielu obiektów (lub surowych wskaźników) wskazujących na to samo miejsce w pamięci. Dlatego twój destruktor zapisany jak powyżej będzie próbował usunąć tę samą pamięć wiele razy.

5

Istnieje kilka różnic:

  1. Wielkość każdego członka musi być znany, gdy jesteś określające klasę. Oznacza to, że musisz zawrzeć nagłówek type i nie możesz po prostu użyć deklaracji forward tak jak w przypadku elementu wskaźnika (ponieważ znany jest rozmiar wszystkich wskaźników). Ma to wpływ na czasy kompilacji i kompilacji dla dużych projektów.

  2. Pamięć dla elementu danych jest część instancja klasy zakrywające, więc zostanie mu przypisane w tym samym czasie, w tym samym miejscu, jak wszystkich innych członków klasy (czy na stosie lub stercie). Ma to wpływ na lokalizację danych - wszystko w tym samym miejscu może potencjalnie prowadzić do lepszego wykorzystania pamięci podręcznej, itp. Przydział stosu będzie prawdopodobnie odrobinę szybszy niż przydział sterty. Zgłoszenie zbyt dużej liczby instancji obiektu może spowodować szybsze zużycie stosu.

  3. Typ wskaźnika jest trudniejszy w zarządzaniu - ponieważ nie jest automatycznie przydzielany lub niszczony razem z klasą, należy to zrobić samemu. To staje się trudne z wieloma elementami wskaźnika - jeśli jesteś new ich wszystkich w konstruktorze, aw połowie procesu istnieje wyjątek, destruktor nie zostaje wywołany i masz wyciek pamięci. Lepiej jest przypisać zmienne kursora do kontenera "inteligentnego wskaźnika" (np. std::auto_ptr) natychmiast, w ten sposób oczyszczanie zostanie automatycznie obsłużone (i nie musisz się martwić o delete je w destruktorze, często oszczędzając ci pisania jeden w ogóle). Ponadto za każdym razem, gdy ręcznie zarządzasz zasobami, musisz się martwić o konstruktorów kopiowania i operatorów przydziału.

+0

Dodawanie kontenerów ze wskaźnikami inteligentnymi - obejmuje (stl/doładowanie) do projektu zwykle zwiększa czas kompilacji o wiele bardziej niż włączenie własnych plików nagłówkowych. Alokacja stosu, które są uważane za tanie, jest dużo szybsza niż alokacja sterty (które są uważane za drogie). Dlaczego należy martwić się o konstruktorów kopii i operatorów przydziału, jeśli ręcznie zarządzasz zasobami? Upewnij się, aby zachować wskaźniki zamiast ich kopiować. (po prostu zadeklaruj, że kopiowanie/przypisanie jest prywatne i nigdy nie popełnisz błędu, aby zrobić źle). – Simon

0

Jeśli zmienna członek powinien żyć poza okres istnienia obiektu, lub jeśli jego własność powinna zostać przeniesiona do innego obiektu, wówczas członek powinien być dynamicznie (heap) przydzielane za pomocą „nowych”. Jeśli nie jest, to często najlepszym wyborem jest uczynienie go bezpośrednim członkiem klasy w celu uproszczenia kodu i zmniejszenia obciążenia na alokatorze pamięci. Alokacja pamięci jest droga.

Powiązane problemy