Różnica między `const shared_ptr <T>` i `shared_ptr <const T>`?

116

Piszę metodę akcesora dla współdzielonego wskaźnika w C ++, która wygląda mniej więcej tak:

class Foo {
public:
    return_type getBar() const {
        return m_bar;
    }

private:
    boost::shared_ptr<Bar> m_bar;
}

Tak więc, aby obsługiwać stałą getBar()zwracanego typu, powinno być a, boost::shared_ptrktóre zapobiega modyfikacji wskazanego przez Barniego. Moje przypuszczenie jest to, że shared_ptr<const Bar>jest to rodzaj chcę wrócić do tego, natomiast const shared_ptr<Bar>uniemożliwia zmiany przeznaczenia wskaźnik się do punktu do innego Bar, ale umożliwiają modyfikację Barże wskazuje ... Jednak nie jestem pewien. Byłbym wdzięczny, gdyby ktoś, kto wie na pewno, mógł to potwierdzić lub poprawić mnie, jeśli źle zrozumiałem. Dzięki!

Dave Lillethun
źródło
3
Dokładnie tak powiedziałeś. Możesz zajrzeć do dokumentacji dla operatorów *i ->to potwierdzić.
syam
2
Jaka jest różnica między T *consti T const *? To samo.
3
@ H2CO3 Wcale nie. Te constzazwyczaj modyfikuje co _precedes, więc T *constjest constwskaźnik Ti T const*jest wskaźnikiem const T. I najlepiej unikać używania, constgdy nic go nie poprzedza.
James Kanze
6
@JamesKanze, o to chodzi w H2CO3: różnica między T *consti T const *jest taka sama jak różnica między const shared_ptr<T>ishared_ptr<const T>
Jonathan Wakely
1
@JamesKanze Oh but yes. T *constjest wskaźnikiem stałej do wartości innej niż stała T, tak jest const shared_ptr<T>. W przeciwieństwie T const *do const Ttego jest wskaźnikiem , który nie jest stałą , tak jest shared_ptr<const T>.

Odpowiedzi:

176

Masz rację. shared_ptr<const T> p;jest podobny do const T * p;(lub równoważnie T const * p;), to znaczy wskazany obiekt jest, constpodczas gdy const shared_ptr<T> p;jest podobny, T* const p;co oznacza, że pjest const. W podsumowaniu:

shared_ptr<T> p;             ---> T * p;                                    : nothing is const
const shared_ptr<T> p;       ---> T * const p;                              : p is const
shared_ptr<const T> p;       ---> const T * p;       <=> T const * p;       : *p is const
const shared_ptr<const T> p; ---> const T * const p; <=> T const * const p; : p and *p are const.

To samo dotyczy weak_ptri unique_ptr.

Cassio Neri
źródło
1
Odpowiedziałeś również na pytanie, które miałem z tyłu głowy, dotyczące regularnych wskaźników (const T * vs. T * const vs. T const *). :) Nie wspomniałem o tym, ponieważ nie chciałem, aby moje pytanie dotyczące SO było zbyt obszerne i to było pytanie dotyczące mojego obecnego zadania. W każdym razie myślę, że teraz bardzo dobrze rozumiem. Dzięki!
Dave Lillethun
9
Cieszę się, że pomogło. Ostatnia wskazówka, o której pamiętam const T* p;', 'T const * p;i T * const p. Postrzegaj *jako separator w tym sensie, że to, co jest, constznajduje się po tej samej stronie *.
Cassio Neri,
5
Moja praktyczna zasada jest taka, że constzawsze odnosi się do rzeczy po lewej stronie. Jeśli nic nie jest po lewej stronie, to jest to po prawej stronie.
hochl
hochi - a co z const T * p; odpowiednik T const * p ;?
Vlad
Cassio, możesz dodać, że w przypadku zwracanego typu const shared_ptr <T> nie można go używać w funkcjach innych niż const, podczas gdy nie jest to prawdą dla wskaźników const.
Vlad
2

boost::shared_ptr<Bar const>zapobiega modyfikacji Barobiektu za pomocą udostępnionego wskaźnika. Jako wartość zwracana, stała w boost::shared_ptr<Bar> constoznacza, że ​​nie można wywołać funkcji innej niż stała na zwróconym obiekcie tymczasowym; gdyby dotyczył prawdziwego wskaźnika (np. Bar* const), zostałby całkowicie zignorowany.

Generalnie nawet tutaj obowiązują zwykłe reguły: constmodyfikuje to, co go poprzedza: in boost::shared_ptr<Bar const>, the Bar; w boost::shared_ptr<Bar> const, jest to instancja (wyrażenie, boost::shared_ptr<Bar>które jest stałą.

James Kanze
źródło
1
@gatopeich Więc możesz to deletezrobić.
Marcin
@Marcin czy mógłbyś się rozwinąć?
gatopeich
1
#Check this simple code to understand... copy-paste the below code to check on any c++11 compiler

#include <memory>
using namespace std;

class A {
    public:
        int a = 5;
};

shared_ptr<A> f1() {
    const shared_ptr<A> sA(new A);
    shared_ptr<A> sA2(new A);
    sA = sA2; // compile-error
    return sA;
}

shared_ptr<A> f2() {
    shared_ptr<const A> sA(new A);
    sA->a = 4; // compile-error
    return sA;
}

int main(int argc, char** argv) {
    f1();
    f2();
    return 0;
}
vivek2k6
źródło
Czy mogę zasugerować użycie std::make_shared() (od C ++ 14).
Joel Bodenmann
0

Chciałbym przeprowadzić prostą demonstrację opartą na odpowiedzi @Cassio Neri:

#include <memory>

int main(){
    std::shared_ptr<int> i = std::make_shared<int>(1);
    std::shared_ptr<int const> ci;

    // i = ci; // compile error
    ci = i;
    std::cout << *i << "\t" << *ci << std::endl; // both will be 1

    *i = 2;
    std::cout << *i << "\t" << *ci << std::endl; // both will be 2

    i = std::make_shared<int>(3);
    std::cout << *i << "\t" << *ci << std::endl; // only *i has changed

    // *ci = 20; // compile error
    ci = std::make_shared<int>(5);
    std::cout << *i << "\t" << *ci << std::endl; // only *ci has changed

}
Jónás Balázs
źródło