In my C++ project I am relying on some libraries that do memory management for me. I make wrapper classes, for ease of use and memory safety, for example the class below. Note that this is a much simplified example, to demonstrate my problem.
#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 */
};
I needed to delete the copy constructor, because otherwise a copy going out of scope would invalidate the original object. Not being allowed to have copies makes the object very impractical to use - everywhere it is used I now need to hold a reference, and I need to manage the object centrally, which partially defeats the purpose of the wrapper class.
Possible solutions I have tried/thought of:
- Copying the data. This is normally not an option though, because it is either not allowed by the library or the data is huge.
- Move constructors. I tried solving the lack of a copy constructor by using a move constructor, but depending on the implementation, either this did not solve the problem or it became effectively a copy constructor, reintroducing the related problems.
- Smart pointers. Then I realized it might be solved by using smart pointers to the wrapper instead of references, as this removes the need to maintain a central copy.
- Wrapping a smart pointer. Or I could wrap a smart pointer to the data instead of a raw pointer. This makes the implementation of the wrapper a little bit more complicated, but also makes it easier to use, giving the classes using it a cleaner interface.
My attempt at the second smart pointer solution applied to the example above:
#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 */
};
Now for the concrete question: is this a good solution? Or are there other, perhaps better solutions?
I am particularly worried about the getData implementation; whether I should return a copy of the shared pointer or a naked pointer and why.
lib_type
? what are its memory conventions?). Perhaps you need to useunique_ptr
.glDeleteProgram
needs to be called when I am done with it. In this case I suppose only the other smart pointer solution is possible. I don't see howunique_ptr
can be of help, because it still won't allow copies.