How do I know if I need to delete something in C ++?

Imagine the following class:

class MyString { public: const char* str; std::size_t str_len; MyString(const char* str, std::size_t str_len) : str { str } , str_len { str_len } {} } 

I am a bit confused about the implementation of the destructor for MyString . My first thought was that it would look like this:

 ~MyString() { delete [] str; } 

But how can I remove str if I cannot be sure that it was highlighted? For example, I could create an instance of MyString as follows:

 const char* c_string = "Hello, World!"; MyString my_string(c_string, 13); 

In this case, I should not delete str because it was not declared on the heap, but if I created an instance of MyString as follows:

 char* char_array = new char[13]{'H','e','l','l','o',',',' ','W','o','r','l','d','!'}; MyString my_string(char_array, 13); 

not deleting str will result in a memory leak (I assume) because it will be declared on the heap. But if I created an instance of MyString as follows:

 char* char_array = new char[13]{'H','e','l','l','o',',',' ','W','o','r','l','d','!'}; MyString my_string(char_array + 3, 10); 

I should not delete str , because although it is on the heap, it was not allocated; he simply points to a part of something else that has been highlighted.

So, how can I be sure that I am not deleting what I should not delete or not deleting, what needs to be deleted? Would the answer be different if MyString used char* instead of const char* s? What if I used MyString my_string = new MyString... ?

Edit: To clarify, I didn't actually write a string class. I use a char array as an array of bytes. I assume that std :: string will not work, since bytes can be 0.

+6
source share
3 answers

There are several different patterns:

  • Always highlight a template. In this approach, the class does not receive ownership of the transferred resource, rather, it makes a copy in the allocated buffer and therefore knows how to free its destructor. The initial parameter belongs to the code that calls the class, and the caller must clear its own data whenever it wants, because the class instance has an independent copy. Example: std::string .

  • Sample-pointer-evader. In this approach, the class takes responsibility and accommodates many pairs of distributor / dellocator, it takes a parameter that is a function or object of a function that knows how to free data. The class destructor will call this object of the function / function of the deleter, performing the correct release (or nothing at all) needed for this particular buffer. Example: std::shared_ptr .

  • Nested ownership template. Here, the class simply stores a pointer or reference to the source data block. The caller still has ownership and responsibility for releasing the data, but it is additionally required to keep this block valid until the created instance of the class exists. This is the lowest run-time overhead, but also the most difficult to track. Example: capturing a reference variable in C ++ 11 lambda.

Whichever of them you use to design your class, make sure you document it so that users of your class do not wonder.

+10
source

But how can I remove str if I cannot be sure that it was highlighted?

You can remove str only if:

  • Document that you will own the pointer passed to the constructor.

  • You document that you will call delete in the memory passed to the constructor.

  • Build class instances with only memory allocated by calling new .

I would also recommend changing the class to use char* instead of const char* .

 class MyString { public: char* str; std::size_t str_len; MyString(char* str, std::size_t str_len) : str { str } , str_len { str_len } {} } 

This will prevent accidental use, for example:

 MyString my_string("Hello, World!", 13); 

And then you have to make sure that you follow the Rule of Three .

+1
source

To clarify the interface, you can use the corresponding smart pointer, for example:

 MyString(std::unique_ptr<const char[]> str, std::size_t str_len) 

or if you do not take ownership, the corresponding string scan, for example:

 MyString(std::experimental::observer_ptr<const char> str, std::size_t str_len) 

Then you no longer doubt the politics of class memory.

+1
source

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


All Articles