Je suis récemment passé à C++ 11 et j'essaie de m'habituer aux bonnes pratiques dans ce domaine. Je finis souvent par avoir à faire face aux problèmes suivants:
class Owner
{
private:
vector<unique_ptr<HeavyResource>> _vectorOfHeavyResources;
public:
virtual const vector<const HeavyResource*>* GetVectorOfResources() const;
};
Cela nécessite que je fasse quelque chose comme ajouter un _returnableVector et traduire les vecteurs source pour pouvoir le renvoyer ultérieurement:
_returnableVector = vector<HeavyResource*>;
for (int i=0; i< _vectorOfHeavyResources.size(); i++)
{
_returnableVector.Push_back(_vectorOfHeavyResources[i].get());
}
Quelqu'un at-il remarqué un problème similaire? Quelles sont vos pensées et solutions? Est-ce que je reçois l'idée de la propriété entière ici?
UPDATE: Voici une autre chose: Et si une classe renvoie le résultat d’un traitement sous la forme vector<unique_ptr<HeavyResource>>
(elle transmet la propriété des résultats à l’appelant), et est supposée être utilisée pour certains traitement ultérieur:
vector<unique_ptr<HeavyResource>> partialResult = _processor1.Process();
// translation
auto result = _processor2.Process(translatedPartialResult); // the argument of process is vector<const HeavyResource*>
Je suggérerais qu'au lieu de conserver et de renvoyer un vecteur non -unique_ptr
ed, vous fournissiez des fonctions pour accéder directement aux éléments. Cela encapsule le stockage de vos ressources; les clients ne savent pas qu'ils sont stockés sous la forme unique_ptr
s, ni qu'ils sont conservés dans une vector
.
Une possibilité consiste à utiliser boost::indirect_iterator
pour déréférencer automatiquement votre unique_ptr
:
using ResourceIterator =
boost::indirect_iterator<std::vector<std::unique_ptr<HeavyResource>>::iterator,
const HeavyResource>;
ResourceIterator begin() { return std::begin(_vectorOfHeavyResources); }
ResourceIterator end() { return std::end(_vectorOfHeavyResources); }
Si vous rencontrez cela souvent, il peut être judicieux d'écrire une classe, qui se comporte comme un unique_ptr, mais passe la constness du pointeur à l'objet vers lequel elle pointe. De cette façon, vous pouvez simplement renvoyer une référence const à votre vecteur.
J'ai fini par écrire ceci une fois et en finir avec ça:
#include <iostream>
#include <vector>
#include <memory>
//unique,const-preserving pointer
template<class T>
class ucp_ptr {
std::unique_ptr<T> ptr;
public:
ucp_ptr() = default;
ucp_ptr(T* ptr) :ptr{ ptr }{};
ucp_ptr(std::unique_ptr<T>&& other) :ptr(std::move(other)){};
T& operator*() { return ptr.get(); }
T const & operator*()const { return ptr.get(); }
T* operator->() { return ptr.get(); }
T const * operator->()const { return ptr.get(); }
};
struct Foo {
int a = 0;
};
int main() {
std::vector<ucp_ptr<Foo>> v;
v.emplace_back(new Foo());
v.emplace_back(std::make_unique<Foo>());
v[0]->a = 1;
v[1]->a = 2;
const std::vector<ucp_ptr<Foo>>& cv = v;
std::cout << cv[0]->a << std::endl; //<-read access OK
//cv[1]->a = 10; //<-compiler error
}
Bien sûr, vous pouvez l’étendre un peu, si vous avez besoin de programmes spéciaux de suppression ou si vous souhaitez ajouter une spécialisation pour la gestion des tableaux, mais il s’agit de la version de base. Je pense aussi avoir vu une version plus raffinée de ceci quelque part ici sur SO, mais je ne la trouve pas pour le moment.
Voici un exemple de la façon dont ceci peut être utilisé dans une classe:
class Bar {
std::vector<ucp_ptr<Foo>> v;
public:
void add(const Foo& foo){
v.Push_back(std::make_unique<Foo>(foo));
}
//modifying elements
void doubleElements() {
for (auto& e : v){
e->a *= 2;
}
}
const std::vector<ucp_ptr<Foo>>& showElements() const{
return v;
}
};
MODIFIER
En ce qui concerne votre mise à jour, vous devez accepter le fait que vector<T>
n’est pas lié à vector<B>
même s’il serait valide de convertir T en B et inversement.
Vous pouvez écrire des adaptateurs qui vous donneront une vue différente des éléments (en lançant chaque élément si nécessaire), mais - mis à part la création d’un nouveau vecteur du type approprié - il n’existe aucun mécanisme général (à ma connaissance de) faire ce que vous voulez.
Vous voudrez peut-être envisager plutôt shared_ptr
, car il reflète probablement ce que vous essayez d'atteindre. Vous voudrez probablement utiliser unique_ptr
dans des portées plus petites, ou chaque fois que vous souhaitez refléter une seule chose à l'aide d'un objet.