Nel mio progetto C ++ mi sto affidando ad alcune librerie che gestiscono la memoria per me. Faccio classi di wrapper, per facilità d'uso e sicurezza della memoria, per esempio la classe qui sotto. Si noti che questo è un esempio molto semplificato, per dimostrare il mio problema.
#include <library>
class Wrapper {
private:
lib_type* data;
public:
Wrapper() : data(library_new()) {
}
Wrapper(const Wrapper& orig) = delete;
~Wrapper() {
library_free(data);
}
const lib_type* getData() const {
return data;
}
/* ... */
/* Lots of functions for using the wrapped object */
};
Avevo bisogno di cancellare il costruttore di copie, perché altrimenti una copia che andava fuori dal campo invaliderebbe l'oggetto originale. Non avere la possibilità di avere copie rende l'oggetto molto poco pratico da usare - ovunque sia usato ora ho bisogno di tenere un riferimento, e ho bisogno di gestire l'oggetto centralmente, che in parte sconfigge lo scopo della classe wrapper.
Possibili soluzioni che ho provato / pensato di:
- Copia dei dati. Normalmente non è un'opzione, perché non è consentita dalla libreria o i dati sono enormi.
- Sposta i costruttori. Ho provato a risolvere la mancanza di un costruttore di copie usando un costruttore di mosse, ma a seconda dell'implementazione, questo non risolveva il problema o diventava effettivamente un costruttore di copie, reintroducendo i problemi correlati .
- Puntatori intelligenti. Poi ho capito che potrebbe essere risolto utilizzando puntatori intelligenti al wrapper anziché ai riferimenti, in quanto ciò elimina la necessità di mantenere una copia centrale.
- Disposizione di un puntatore intelligente. Oppure potrei racchiudere un puntatore intelligente sui dati anziché un puntatore non elaborato. Ciò rende l'implementazione del wrapper un po 'più complicata, ma rende anche più facile l'uso, dando alle classi che lo utilizzano un'interfaccia più pulita.
Il mio tentativo alla seconda soluzione di puntatore intelligente applicata all'esempio sopra:
#include <library>
class Deleter {
public:
void operator()(lib_type* p) const {
library_free(p);
}
};
class Wrapper {
private:
shared_ptr<lib_type> data;
public:
Wrapper() : data(library_new(), Deleter()) {
}
Wrapper(const Wrapper& orig) : data(orig.data) {
}
~Wrapper() {
}
const lib_type* getData() const {
return *data;
}
/* ... */
/* Lots of functions for using the wrapped object */
};
Ora per la domanda concreta: questa è una buona soluzione? O ci sono altre soluzioni, forse migliori?
Sono particolarmente preoccupato per l'implementazione di getData; se dovrei restituire una copia del puntatore condiviso o un puntatore nudo e perché.