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 } {} }
我对为MyString实现析构函数感到困惑.我的第一个想法是它看起来像这样:
~MyString() { delete [] str; }
但是,如果我不能确定它已被分配,我怎么能删除str?例如,我可以像这样创建一个MyString实例:
const char* c_string = "Hello,World!"; MyString my_string(c_string,13);
在这种情况下我不应该删除str,因为它没有在堆上声明,但如果我创建了一个MyString实例,如下所示:
char* char_array = new char[13]{'H','e','l','o',',' ','W','r','d','!'}; MyString my_string(char_array,13);
不删除str会导致内存泄漏(我假设),因为它将在堆上声明.但是如果我像这样创建一个MyString实例:
char* char_array = new char[13]{'H','!'}; MyString my_string(char_array + 3,10);
我不应该删除str,因为虽然它在堆上,但它还没有被分配;它只是指向已经分配的其他东西的一部分.
那么我怎么能确定我没有删除我不应该删除的内容或者没有删除需要删除的内容?如果MyString使用char * s而不是const char * s,答案是否会有所不同?如果我使用MyString my_string = new MyString …怎么办?
编辑:为了澄清,我实际上并没有写一个字符串类.我使用char数组作为字节数组.我假设std :: string不起作用,因为字节可能是0.
解决方法
>始终分配模式.在这种方法中,类不接受传入资源的所有权,而是在它分配的缓冲区中创建一个副本,因此知道如何在其析构函数中释放.原始参数由调用类的代码拥有,并且调用者应该在需要时清理自己的数据,因为类实例具有独立的副本.示例:std :: string.
>调用者指定的删除模式.在这种方法中,类确实拥有所有权,并且为了容纳各种allocator / deallocator对,它接受一个参数,该参数是一个知道如何释放数据的函数或函数对象.类析构函数将调用此删除函数/函数对象,执行该特定缓冲区所需的正确释放(或根本不执行).示例:std :: shared_ptr.
>嵌套所有权模式.这里,类只保留指向原始数据块的指针或引用.调用者仍然拥有所有权并且有责任释放数据,但是除了它创建的类实例存在之外,还需要保持该块有效.这是运行时最低的开销,但也是最难跟踪的开销.示例:C 11 lambda中的引用变量捕获.
无论您将哪种类型设计用于课程设计,请确保将其记录下来,以便您的班级用户不会感到疑惑.