Historically, management wanted the task to be completed quickly. To do this, programmers preserve the beauty and purity of the code. This post appeared as a reminder of the rarely used innovations in C ++ 11 - smart points that allow you to specify a functor for freeing resources.
For example, let's take the file stream FILE from stdio.h, which they love for simplicity and speed, we will try to add to it the beauty and basic guarantee with exceptions:
unique_ptr<FILE, decltype(&fclose)> my_file(fopen("test.txt", "w"), &fclose); if(my_file) fwrite("test", 4, 1, my_file.get());
As a result, the code depends only on STL and requires a small modification of file accesses, it is written quickly, it looks modern. That's how RAII turned out to be pure.
How it works?
The fopen function returns a pointer to an object of type FILE, which is stored in the variable my_file along with a pointer to the function fclose. In this way, ownership of this file stream is transferred to a local variable.
When will the fclose function be called automatically?
- When leaving the scope of a variable (for example, from a function).
- When an exception occurs after creating my_file.
- When the assignment function is called on my_file.
- When calling my_file.reset ().
What are the overhead costs?
- The programmer needs to complicate the creation of the file, remove the call to fclose and supplement with the call to unique_ptr <...> :: get () all references to the file.
- In the worst case, the compiler will need a memory location to hold a pointer to the file deletion function. At best, it will simply place the fclose call in the right place for you, fully optimizing the my_file object.
What are the advantages of this approach?
- As with any smart pointer, you explicitly indicate how you own the object. In this case, it is indicated that the object is not shared (unique_ptr).
- You can get rid of excess copy-paste by declaring your type like this:
typedef unique_ptr<FILE, decltype(&fclose)> MyFileType;
- If you use a lot of files, it makes sense to write a small wrapper.
MyFileType MakeFile(const char* filename, const char* mode) { return unique_ptr<FILE, decltype(&fclose)>(fopen(filename, mode), &fclose); }
... and use it like this:
auto my_file = MakeFile("test.txt", "w")
- Allows you to get rid of writing extra code in the destructor. Why superfluous? You have already indicated to the compiler how you want to manage this resource and now it’s his job.
- You can use objects of type MyFileType in standard STL containers:
vector<MyFileType> my_files; my_files.push_back(MakeFile("test.txt", "w"));
... and do not waste your time on controlling the lifetime of objects. In C ++ 11, vector <MyFileType> can be safely returned from a function.
Here are some more ideas from the C Runtime Library:
Those who are puzzled or keen on optimizing for Windows know that access to aligned data is faster. So you can create a pointer to memory aligned to 16 bytes using the Microsoft Visual C Runtime library:
unique_ptr<char[], decltype(&::_aligned_free)> my_buffer((char*)(_aligned_malloc(512, 16)), &_aligned_free); my_buffer[0] = 'x';
Once you write a template:
template<typename T> unique_ptr<T[], decltype(&::_aligned_free)> MakeAlignedBuffer(size_t element_count, size_t alignment = alignment_of<T>::value) { return unique_ptr<T[], decltype(&::_aligned_free)> (reinterpret_cast<T*>(_aligned_malloc(element_count*sizeof(T), alignment)), &_aligned_free); }
You can forget about the errors of allocating and deleting memory with different functions (created with new [] in one module, deleted with delete in another).
And what to do if a certain WinAPI resource owns several objects?
For example, consider the situation when several different objects in a GUI application use functions that are in a dynamically loaded DLL. In this case, it is not so easy to program the timely unloading of the library as we would like:
Loading the library ...
auto my_module = shared_ptr<HMODULE>(new HMODULE(LoadLibrary(_T("my_library.dll"))), [](HMODULE* instance){ FreeLibrary(*instance)
Next, distribute my_module objects ...
module_owner1.set_module(my_module); module_owner2.set_module(my_module);
We use the necessary functions in the object ...
if(my_module && *my_module) { auto func1 = GetProcAddress(*my_module, "MyFunc")
When we stop using the functions and the object reference count is zero, the my_module object will be called FreeLibrary function and the object will be deleted.
')
How to use lambda function in unique_ptr?
You need to use the function template like this:
auto my_instance = std::unique_ptr<HMODULE, function<void(HMODULE*)>> (new HMODULE(LoadLibrary(_T("my_library.dll"))), [](HMODULE* instance){ FreeLibrary(*instance); });
Conclusion
Dear readers, remember that any technology is developed for a specific purpose and should not be used where its use is not justified, i.e. Do not rush to replace all the pointers to smart pointers without thinking about the need and not analyzing the consequences. There are also disadvantages to these approaches, and they have been repeatedly discussed in Habré. Be professional.
Thank.