I’ve written a function that loads the bytes off a file and returns a FileData struct that contains the byte buffer and the length of the buffer.
I want the buffer to be deleted as soon as it’s consumed and thrown out of scope.
I’m having trouble getting it to compile due to various casting errors. Also, I’m not sure whether the buffer is being moved correctly rather than copied. I don’t mind the FileData struct itself being copied, since it’s maybe 16 bytes at most.
In general, how do you use smart pointers as class/struct fields? Is that even something you’d do?
This is a bit of a nebulous question, I know, but since I’m having some conceptual difficulties with smart pointers in general, I’m hoping that this example will help me in the right direction.
Here’s what I’ve got so far:
struct FileData
{
unique_ptr<char[]> buf;
unsigned int len;
};
FileData LoadFile(string filename)
{
ifstream str;
str.open(filename, ios::binary);
str.seekg(0, ios::end);
auto len = str.tellg();
str.seekg(0, ios::beg);
char* buf = new char[len];
str.read(buf, len);
str.close();
FileData d = { unique_ptr<char[]>(buf), len };
return d;
}
Edit: Since some people are curious about the error message that I get with this current code, here it is:
error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
Your code is fine, except for one little detail:
The reason it doesn’t compile for you is that your compiler does not yet implement the automatic generation of the special move members. In a fully C++11 conforming compiler your
FileDatawould behave as if:The defaulted move constructor simply move constructs each member (and similarly for the defaulted move assignment).
When returning
dfromLoadFile, there is an implicit move that takes place which will bind to the implicitly defaulted move constructor.Use of
vector<char>orstringas others have suggested will also work. But there is nothing wrong with your code as far as C++11 is concerned.Oh, I might tweak it like so: I like to get my resources owned as quickly as possible:
If you need to explicitly define the
FileDatamove members, it should look like:Oh, which brings me to another point. The defaulted move members are not exactly correct since they don’t set
lento 0 in the source. It depends on your documentation if this is a bug or not.~FileData()doesn’t requirelento reflect the length of the buffer. But other clients might. If you define a moved-fromFileDataas not having a reliablelen, then the defaulted move members are fine, else they aren’t.