Using smart pointers in a structure or class

I wrote a function that loads bytes from a file and returns a FileData structure that contains a byte buffer and a buffer length.

I want the buffer to be deleted as soon as it is destroyed and thrown out of the area.

I am having trouble compiling it due to various sending errors. Also, I'm not sure if the buffer is moving correctly and not being copied. I do not mind copying the FileData structure itself, since it can be no more than 16 bytes.

In general, how do you use smart pointers as class / structure fields? Is that even what you would do?

This is a bit of a vague question, I know, but since I have some conceptual difficulties with smart pointers in general, I hope this example helps me in the right direction.

Here is what I have 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 interested in learning about the error message 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>' 
+6
source share
3 answers

Your code is fine except for one small detail:

 struct FileData { unique_ptr<char[]> buf; <del>unsigned int</del> <ins>streamoff</ins> len; }; 

The reason it does not compile for you is because your compiler does not yet implement the automatic creation of special relocation elements. In a fully C ++ 11 compiler, your FileData will behave like this:

 struct FileData { unique_ptr<char[]> buf; streamoff len; FileData(FileData&&) = default; FileData& operator=(FileData&&) = default; FileData(const FileData&) = delete; FileData& operator=(const FileData&) = delete; ~FileData() = default; }; 

The default move constructor simply moves the constructions of each member (and similarly for the default assignment for the move).

When d returned from the LoadFile , an implicit move occurs that will bind to the implicitly unlocked move constructor.

Using vector<char> or string , as others have suggested, will also work. But there is nothing wrong with your code as far as C ++ 11 is concerned.

Oh, I can configure it like this: I like to use my resources as quickly as possible:

 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); FileData d = {unique_ptr<char[]>(new char[len]), len}; str.read(d.buf.get(), d.len); str.close(); return d; } 

If you need to explicitly define FileData move elements, this should look like this:

 struct FileData { unique_ptr<char[]> buf; streamoff len; FileData(FileData&& f) : buf(std::move(f.buf)), len(f.len) { f.len = 0; } FileData& operator=(FileData&& f) { buf = std::move(f.buf); len = f.len; f.len = 0; return *this; } }; 

Oh, which brings me to another point. By default, move elements do not match exactly , since they do not set len to 0 in the source. It depends on your documentation if this is a mistake or not. ~FileData() does not require len to reflect the length of the buffer. But other customers can. If you have defined relocated from FileData as not having a reliable len , then the elements with the default, which were the default, are accurate, otherwise they will not.

+5
source

I would probably use std::vector instead of std:::unique_ptr<char[]> if you don't mind copying std::vector when returning FileData :

 struct FileData { vector<char> buf; }; 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); FileData d; d.buf.resize(len); str.read(&(d.buf)[0], len); str.close(); return d; } 

Alternatively, to avoid copying, the caller can pass FileData as a parameter to the function instead of the return value:

 struct FileData { vector<char> buf; }; void LoadFile(string filename, FileData &data) { ifstream str; str.open(filename, ios::binary); str.seekg(0, ios::end); auto len = str.tellg(); str.seekg(0, ios::beg); data.buf.resize(len); str.read(&(data.buf)[0], len); str.close(); } 
+2
source

How to use std :: string as a buffer. You have everything you want:

  • the link is counted, not copied
  • disappears from sight
  • save arbitrary number of arbitrary bytes

People will vote for it because this is not the original purpose of using the string; perhaps derive a class (or wrap it) and call it "buffer"

-1
source

Source: https://habr.com/ru/post/913091/


All Articles