Différence entre `const shared_ptr <T>` et `shared_ptr <const T>`?

116

J'écris une méthode d'accesseur pour un pointeur partagé en C ++ qui ressemble à ceci:

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

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

Donc, pour prendre en charge la const-ness du getBar()type de retour devrait être un boost::shared_ptrqui empêche la modification du Barpoint vers lequel il pointe. Je suppose que shared_ptr<const Bar>c'est le type que je veux retourner pour faire cela, alors que const shared_ptr<Bar>cela empêcherait la réaffectation du pointeur lui-même pour pointer vers un autre Barmais permettrait de modifier le Barpoint vers lequel il pointe ... Cependant, je ne suis pas sûr. J'apprécierais que quelqu'un qui sait avec certitude puisse confirmer cela ou me corriger si je me trompe. Merci!

Dave Lillethun
la source
3
C'est exactement ce que tu as dit. Vous pouvez consulter la documentation des opérateurs *et ->le confirmer.
syam
2
Quelle est la différence entre T *constet T const *? Le même.
3
@ H2CO3 Pas du tout. Le constmodifie normalement ce que _précède, ainsi T *constest un constpointeur vers Tet T const*est un pointeur vers const T. Et il vaut mieux éviter d'utiliser constsans rien le précédant.
James Kanze
6
@JamesKanze, c'est le point de H2CO3: la différence entre T *constet T const *est la même que la différence entre const shared_ptr<T>etshared_ptr<const T>
Jonathan Wakely
1
@JamesKanze Oh mais oui. T *constest un pointeur const vers non-const T, il en est de même const shared_ptr<T>. En revanche, T const *est un pointeur non const sur const T, il en est de même shared_ptr<const T>.

Réponses:

176

Vous avez raison. shared_ptr<const T> p;est similaire à const T * p;(ou, de manière équivalente, T const * p;), c'est-à-dire que l'objet pointé est constalors que const shared_ptr<T> p;est similaire à T* const p;ce qui signifie que pc'est const. En résumé:

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.

La même chose vaut pour weak_ptret unique_ptr.

Cassio Neri
la source
1
Vous avez également répondu à une question que j'avais dans le fond de ma tête sur les pointeurs réguliers (const T * vs T * const vs T const *). :) Je n'ai pas mentionné cela parce que je ne voulais pas que ma question sur SO soit trop large, et c'était la question pertinente pour ma tâche actuelle. Quoi qu'il en soit, je pense que je comprends très bien maintenant. Merci!
Dave Lillethun
9
Je suis content que cela ait aidé. Une dernière astuce dont je me souviens sur const T* p;', 'T const * p;et T * const p. Voyez le *comme un séparateur dans le sens où ce qui est constest ce qui est du même côté du *.
Cassio Neri
5
Ma règle de base est que se constréfère toujours à la chose sur le côté gauche de celui-ci. Si rien n'est sur la gauche, c'est la chose sur le côté droit.
hochl
hochi - que diriez-vous de const T * p; équivalent à T const * p ;?
Vlad
Cassio, vous pouvez ajouter que dans le cas du type renvoyé const shared_ptr <T>, il ne peut pas être utilisé dans des fonctions non const alors que ce n'est pas vrai pour les pointeurs const.
Vlad
2

boost::shared_ptr<Bar const>empêche la modification de l' Barobjet via le pointeur partagé. En tant que valeur de retour, le const in boost::shared_ptr<Bar> constsignifie que vous ne pouvez pas appeler une fonction non-const sur le temporaire renvoyé; s'il s'agissait d'un vrai pointeur (par exemple Bar* const), il serait complètement ignoré.

En général, même ici, les règles habituelles s'appliquent: constmodifie ce qui le précède: in boost::shared_ptr<Bar const>, the Bar; in boost::shared_ptr<Bar> const, c'est l'instanciation (l'expression boost::shared_ptr<Bar>qui est const.

James Kanze
la source
1
@gatopeich Pour que vous le puissiez delete.
Marcin
@Marcin pourriez-vous expliquer?
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
la source
Puis-je suggérer l'utilisation de std::make_shared()(depuis C ++ 14).
Joel Bodenmann
0

J'aimerais une simple démonstration basée sur la réponse de @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
la source