Prologue
El const
modifie le comportement de std::shared_ptr
de la même manière qu'elle affecte les anciens pointeurs C.
Les pointeurs intelligents doivent être gérés et stockés en utilisant à tout moment les bons qualificatifs afin de prévenir, d'appliquer et d'aider les programmeurs à les traiter correctement.
Réponses
- Lorsque quelqu'un passe un
shared_ptr<const T>
dans la classe, dois-je le stocker en tant que shared_ptr<T>
ou shared_ptr<const T>
à l'intérieur du vecteur et de la carte ou dois-je changer la carte, les types de vecteurs ?
Si votre API accepte un shared_ptr<const T>
le contrat tacite entre l'appelant et vous-même stipule que vous n'êtes PAS autorisé à modifier l'adresse de l'appelant. T
l'objet pointé par le pointeur, vous devez donc le conserver comme tel dans vos conteneurs internes, par ex. std::vector<std::shared_ptr<const T>>
.
De plus, votre module ne devrait JAMAIS pouvoir/être autorisé à retourner std::shared_ptr<T>
même si l'on peut y parvenir par programmation (voir ma réponse à la deuxième question pour savoir comment).
- Est-il préférable d'instancier les classes comme suit :
MyExample<const int>
? Cela semble indûment restrictif, car je ne peux jamais renvoyer un fichier shared_ptr<int>
?
Ça dépend :
-
Si vous avez conçu votre module de manière à ce que les objets qui lui sont passés ne soient plus modifiés à l'avenir, utilisez le module const T
comme type sous-jacent.
-
Si vous le faites, votre module devrait être capable de retourner des données non-constantes. T
vous devez utiliser T
comme type sous-jacent et vous aurez probablement deux récupérateurs différents, l'un qui renvoie des objets mutables ( std::shared_ptr<T>
) et un autre qui renvoie des objets non mutables ( std::shared_ptr<const T>
).
Et, même si j'espère que nous nous sommes mis d'accord sur le fait que vous ne devrait pas retourner std::shared_ptr<T>
si vous avez un const T
ou std::shared_ptr<const T>
vous peut :
const T a = 10;
auto a_ptr = std::make_shared<T>(const_cast<T>(a));
auto b_const_ptr = std::make_shared<const T>();
auto b_ptr = std::const_pointer_cast<T>(b_const_ptr);
Exemple complet
Considérons l'exemple suivant qui couvre toutes les permutations possibles de const
avec std::shared_ptr
:
struct Obj
{
int val = 0;
};
int main()
{
// Type #1:
// ------------
// Create non-const pointer to non-const object
std::shared_ptr<Obj> ptr1 = std::make_shared<Obj>();
// We can change the underlying object inside the pointer
ptr1->val = 1;
// We can change the pointer object
ptr1 = nullptr;
// Type #2:
// ------------
// Create non-const pointer to const object
std::shared_ptr<const Obj> ptr2 = std::make_shared<const Obj>();
// We cannot change the underlying object inside the pointer
ptr2->val = 3; // <-- ERROR
// We can change the pointer object
ptr2 = nullptr;
// Type #3:
// ------------
// Create const pointer to non-const object
const std::shared_ptr<Obj> ptr3 = std::make_shared<Obj>();
// We can change the underlying object inside the pointer
ptr3->val = 3;
// We can change the pointer object
ptr3 = nullptr; // <-- ERROR
// Type #4:
// ------------
// Create const pointer to non-const object
const std::shared_ptr<const Obj> ptr4 = std::make_shared<const Obj>();
// We can change the underlying object inside the pointer
ptr4->val = 4; // <-- ERROR
// We can change the pointer object
ptr4 = nullptr; // <-- ERROR
// Assignments:
// ------------
// Conversions between objects
// We cannot assign to ptr3 and ptr4, because they are const
ptr1 = ptr4 // <-- ERROR, cannot convert 'const Obj' to 'Obj'
ptr1 = ptr3;
ptr1 = ptr2 // <-- ERROR, cannot convert 'const Obj' to 'Obj'
ptr2 = ptr4;
ptr2 = ptr3;
ptr2 = ptr1;
}
Note : Ce qui suit est vrai lors de la gestion de tous les types de pointeurs intelligents. L'affectation des pointeurs peut être différente (par exemple, lors de la gestion de unique_ptr
), mais le concept est le même.