2016-01-02 16 views
10

Chcę mieć klasę ze zmienną składową wskaźnika. Wskaźnik ten powinien wskazywać obiekt, który może być przydzielany do stosu lub przydzielany stosem. Ten wskaźnik nie powinien jednak być własnością. Innymi słowy, żadne usuwanie nie powinno być wywoływane, gdy wskaźnik wykracza poza zakres. Myślę, że surowy wskaźnik może rozwiązać problem ... Jednak nie jestem pewien, czy istnieje lepsze podejście C++ 11 niż surowe wskaźniki?Wskaźnik do obiektu stosu bez własności

Przykład:

class foo{ 
public: 
    bar* pntr 
}; 

int main(){ 
    bar a; 
    foo b; 
    b.pntr=&a; 
} 

Odpowiedz

17

Surowe wskaźniki są tutaj idealnie w porządku. C++ 11 nie ma żadnego innego "głupiego" inteligentnego wskaźnika, który zajmuje się obiektami nie będącymi właścicielami, więc nie można używać inteligentnych wskaźników C++ 11. Jest to propozycja dla „głupiej” inteligentnego wskaźnika dla osób niebędących własnością obiektów: (. Dzięki @ T.C. za podpowiedź)

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf

już realizowany eksperymentalnie jako std::experimental::observer_ptr.

Inną alternatywą jest zastosowanie inteligentnego wskaźnika z niestandardowym Deleter, że nic nie robi:

#include <memory> 

int main() 
{ 
    int a{42}; 

    auto no_op = [](int*){}; 
    std::unique_ptr<int, decltype(no_op)> up(&a, no_op); 
} 

lub, jak wspomniano przez @ T.C.. w komentarzu: std::reference_wrapper.

Jak wspominają wyścigi @Lightness na Orbicie, rozwiązaniem może być również std::weak_ptr, ponieważ ten drugi jest również nie-właścicielem inteligentnego wskaźnika. Jednak std::weak_ptr może być zbudowany tylko z std::shared_ptr lub innego std::weak_ptr. Poważnym minusem jest to, że std::shared_ptr jest "ciężkim" obiektem (z powodu wewnętrznego mechanizmu zliczania odnośników). Zauważ, że nawet w tym przypadku std::shared_ptr musi mieć trywialny niestandardowy deletor, w przeciwnym razie uszkodzony stos dla wskaźników do zmiennych automatycznych.

+3

Powinieneś naprawdę połączyć się z [ostatnią wersją (N4282)] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf) lub [' std :: experimental :: observer_ptr' (http://en.cppreference.com/w/cpp/experimental/observer_ptr). Ponadto "reference_wrapper" jest opcją, jeśli wskaźnik nigdy nie powinien mieć wartości NULL. –

+0

@ T.C. Dzięki, gotowe. – vsoftco

+0

@ T.C. Btw, czy znasz jakieś wdrożenie, które ma ""? Próbuję g ++ 5.3 i clang ++ 3.7, ale nagłówek nie jest zaimplementowany. – vsoftco

3

Korzystanie z surowego wskaźnik jest doskonale ok tutaj jak nie zamierzają pozwolić wskaźnik mają własność zasobu wskazywanego.

-3

Po prostu przydziel obiekt dynamicznie i użyj shared_ptr. Tak, faktycznie to usunie rzecz, ale tylko jeśli jest ostatnią z referencją. Co więcej, uniemożliwia to również jej usunięcie przez innych. Jest to słuszne, zarówno w celu uniknięcia wycieków pamięci, jak i zwisających wskaźników. Sprawdź również powiązane weap_ptr, które możesz ewentualnie wykorzystać na swoją korzyść, jeśli wymagania życia dla pointee są różne.

+2

To nie jest poprawne. Jeśli wskaźnik znajduje się w zmiennej automatycznej, to spowoduje to uszkodzenie stosu. – vsoftco

+0

Dobrze, nie miałem jasności co do tej części. W każdym razie sugerowałbym przeczytanie instrukcji, aby uniknąć podobnych pułapek. –

+0

Zgadzam się z koncepcjami (Urlich i inni) w tej części: C++ ma za dużo modeli pamięci (dla dużych aplikacji, ogólnych obliczeń), zgadzam się, że to "overdesign" (pewnego rodzaju) może być zredukowane na przykład przy użyciu przydzielonej części danych ze statycznego/przydzielony inteligentny wskaźnik/etc. Wręcz przeciwnie: małe projekty sprzętowe wymagają bardziej agresywnego kodu. Stos BTW jest zasobem o ograniczonej ilości zasobów na wiele uC –

0

Jeśli przez "lepsze podejście" rozumiesz "bezpieczniejsze podejście", to tak, zaimplementowałem tutaj "inteligentny wskaźnik", który nie jest właścicielem: https://github.com/duneroadrunner/SaferCPlusPlus. (Shameless alert wtyczki, ale myślę, że to istotne tutaj.) Tak Twój kod będzie wyglądać następująco:

#include "mseregistered.h" 
... 

class foo{ 
public: 
    mse::TRegisteredPointer<bar> pntr; 
}; 

int main(){ 
    mse::TRegisteredObj<bar> a; 
    foo b; 
    b.pntr=&a; 
} 

TRegisteredPointer jest „mądrzejszy” od surowych wskaźników w tym, że nie wie, kiedy cel zostanie zniszczony. Na przykład:

int main(){ 
    foo b; 
    bar c; 
    { 
     mse::TRegisteredObj<bar> a; 
     b.pntr = &a; 
     c = *(b.pntr); 
    } 
    try { 
     c = *(b.pntr); 
    } catch(...) { 
     // b.pntr "knows" that the object it was pointing to has been deleted so it throws an exception. 
    }; 
} 

TRegisteredPointer generalnie ma niższy koszt wydajności niż powiedz, std :: shared_ptr. Znacznie niższe, gdy masz możliwość przydzielenia obiektu docelowego na stosie. Jest to wciąż całkiem nowa wersja i nie jest jeszcze dobrze udokumentowana, ale biblioteka zawiera skomentowane przykłady jej użycia (w pliku "msetl_example.cpp", w dolnej połowie).

Biblioteka zapewnia również TRegisteredPointerForLegacy, która jest nieco wolniejsza niż TRegisteredPointer, ale może być używana jako zastępczy wskaźnik surowych wskaźników w prawie każdej sytuacji. (W szczególności może być użyty, zanim typ docelowy zostanie całkowicie zdefiniowany, co nie ma miejsca w przypadku TRegisteredPointer.)

Pod względem sentymentu twojego pytania, myślę, że jest ono ważne. Do tej pory programiści C++ powinni przynajmniej mieć możliwość uniknięcia niepotrzebnego ryzyka nieprawidłowego dostępu do pamięci. Surowe wskaźniki również mogą być ważną opcją, ale myślę, że zależy to od kontekstu. Jeśli jest to złożone oprogramowanie, w którym bezpieczeństwo jest ważniejsze niż wydajność, bezpieczniejsza alternatywa może być lepsza.

0

Problem z surowym wskaźnikiem polega na tym, że nie można stwierdzić, czy nadal wskazuje on prawidłowy obiekt. Na szczęście std::shared_ptr ma aliasing constructor, której można użyć do efektywnego utworzenia std::weak_ptr dla członka klasy z automatycznym czasem przechowywania. Przykład:

#include <iostream> 
#include <memory> 

using namespace std; 

struct A { 
    int x; 
}; 

void PrintValue(weak_ptr<int> wp) { 
    if (auto sp = wp.lock()) 
     cout << *sp << endl; 
    else 
     cout << "Object is expired." << endl; 
} 

int main() { 

    shared_ptr<A> a(new A); 
    a->x = 42; 
    weak_ptr<int> wpInt (shared_ptr<int>(a, &a->x)); 

    PrintValue(wpInt); 
    a.reset(); //a->x has been destroyed, wpInt no longer points to a valid int 
    PrintValue(wpInt); 

    return 0; 
} 

reprodukcje: czy upłynął

obiektów.

Główną zaletą tego podejścia jest to, że nie przeszkadza weak_ptr przedmiot przed pójściem poza zakresem i zostać usunięty, ale jednocześnie można go bezpiecznie wykryć, gdy obiekt nie jest już ważny. Wadami są zwiększony narzut inteligentnego wskaźnika i fakt, że ostatecznie potrzebujesz obiektu do obiektu shared_ptr. To znaczy. nie można tego zrobić wyłącznie z obiektami przydzielonymi na stosie.

Powiązane problemy