简体   繁体   English

我怎么知道我是否需要删除C ++中的东西?

[英]How can 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'm a bit confused about implementing a destructor for MyString . 我对为MyString实现析构函数感到困惑。 My first thought was that it would look like this: 我的第一个想法是它看起来像这样:

~MyString() {
    delete [] str;
}

But how can I delete str if I can't be sure that it was allocated? 但是,如果我不能确定它已被分配,我怎么能删除str? For example, I could create an instance of MyString like this: 例如,我可以像这样创建一个MyString实例:

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

in which case I shouldn't delete str because it was not declared on the heap, but if I created an instance of MyString like this: 在这种情况下我不应该删除str因为它没有在堆上声明,但如果我创建了一个MyString实例,如下所示:

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 would cause a memory leak (I assume) because it would be declared on the heap. 不删除str会导致内存泄漏(我假设),因为它将在堆上声明。 But if I created an instance of MyString like this: 但是如果我像这样创建一个MyString实例

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

I shouldn't delete str because although it's on the heap, it hasn't been allocated; 不应该删除str因为虽然它在堆上,但它还没有被分配; it just points to part of something else that's been allocated. 它只是指向已经分配的其他东西的一部分。

So how can I be sure that I'm not deleting something that I shouldn't be deleting or failing to delete something that needs to be deleted? 那么我怎么能确定我没有删除我不应该删除的内容或者没有删除需要删除的内容? Would the answer be different if MyString used char* s instead of const char* s? 如果MyString使用char* s而不是const char* s,答案是否会有所不同? What if I used MyString my_string = new MyString... ? 如果我使用MyString my_string = new MyString...怎么办?

Edit: To clarify, I'm not actually writing a string class. 编辑:为了澄清,我实际上并没有写一个字符串类。 I'm using a char array as a byte array. 我使用char数组作为字节数组。 I assume std::string wouldn't work, since the bytes could be 0. 我假设std :: string不起作用,因为字节可能是0。

There are a couple different patterns that apply: 有几种不同的模式适用:

  1. The always-allocate pattern. 始终分配模式。 In this approach, the class doesn't take ownership of the passed-in resource, rather it makes a copy in a buffer it allocated and therefore knows how to deallocate in its destructor. 在这种方法中,类不接受传入资源的所有权,而是在它分配的缓冲区中创建一个副本,因此知道如何在其析构函数中释放。 The original parameter is owned by the code calling the class, and that caller should clean up its own data whenever it wants, because the class instance has an independent copy. 原始参数由调用类的代码拥有,并且调用者应该在需要时清理自己的数据,因为类实例具有独立的副本。 Example: std::string . 示例: std::string

  2. The caller-specified-deleter pattern. 调用者指定的删除模式。 In this approach, the class does take ownership, and to accomodate a variety of allocator/deallocator pairs, it accepts a parameter which is a function or function object that knows how to deallocate the data. 在这种方法中,类确实拥有所有权,并且为了容纳各种allocator / deallocator对,它接受一个参数,该参数是一个知道如何释放数据的函数或函数对象。 The class destructor will call this deleter function/function object, performing the correct deallocation (or none at all) needed for that particular buffer. 类析构函数将调用此删除函数/函数对象,执行该特定缓冲区所需的正确释放(或根本不执行)。 Example: std::shared_ptr . 示例: std::shared_ptr

  3. The nested-ownership pattern. 嵌套所有权模式。 Here, the class just keeps a pointer or reference to the original block of data. 这里,类只保留指向原始数据块的指针或引用。 The caller still has ownership and the responsibility to free the data, but it additionally is required to keep that block valid for as long as the class instance it created exists. 调用者仍然拥有所有权并且有责任释放数据,但是除了它创建的类实例存在之外,还需要保持该块有效。 This is the lowest overhead at runtime, but also the most difficult to keep track of. 这是运行时最低的开销,但也是最难跟踪的开销。 Example: by-reference variable capture in a C++11 lambda. 示例:C ++ 11 lambda中的by-reference变量捕获。

Whichever of these you use for your class design, make sure you document it so the users of your class aren't left wondering. 无论您使用哪种类型设计,请确保将其记录下来,以便您的班级用户不会感到疑惑。

But how can I delete str if I can't be sure that it was allocated? 但是,如果我不能确定它已被分配,我怎么能删除str

You can delete str only if: 您只能在以下情况下删除str

  1. You document that you will be taking ownership of the pointer being passed to the constructor. 您将记录您将获取传递给构造函数的指针的所有权。

  2. You document that you will be calling delete on the memory passed to the constructor. 您记录了您将在传递给构造函数的内存上调用delete

  3. Construct instances of the class only with memory allocated by call to new . 仅通过调用new来分配内存来构造类的实例。

I would also recommend changing the class to use char* instead of const char* . 我还建议更改类使用char*而不是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 }
    {}
}

That would prevent accidental usages like: 这可以防止意外使用,例如:

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

And then, you have to make sure that you follow The Rule of Three . 然后,你必须确保遵循三法则

To clarify interface, you may use appropriate smart pointer, as for example: 为了阐明界面,您可以使用适当的智能指针,例如:

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

or if you don't take ownership, appropriate string-view, as for example: 或者,如果您没有获得所有权,请使用适当的字符串视图,例如:

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

Then you no longer doubt on memory policy of the class. 然后你不再怀疑班级的记忆政策。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM