std::string的内存泄漏取决于字符串大小



我在C++中得到了一个内存泄漏,这取决于字符串大小。在下面的代码中,如果字符串大小小于15,valgrind不会显示泄漏,但是如果字符串大小大于15,我会出现内存泄漏:

#include <string>
#include <memory>
#include <iostream>
class AuthBase {
public:
virtual void foo() = 0;
};
class APIAuthetication : public AuthBase {
public:
APIAuthetication(const std::string api_key, const std::string api_secret) : m_api_key(api_key),
m_api_secret(api_secret) {}

virtual ~APIAuthetication() {}
void foo() {}
private:
const std::string m_api_key;
const std::string m_api_secret;
};
class ClientBase {
public:
virtual void bar() = 0;
};
class Client: public ClientBase {
public:
Client(AuthBase* auth) : m_auth(auth) {}
~Client() {}
void bar() {}
private:
std::unique_ptr<AuthBase> m_auth;
};
class ClientAPI : public Client {
public:
ClientAPI(const std::string api_key, const std::string api_secret) : 
Client((new APIAuthetication(api_key, api_secret))) {}
virtual ~ClientAPI() {}
};
int main(void) {
/* This runs fine. String size == 15 */
ClientAPI cli("123456789101112", "123456789101112");
/* Memory leak. String size > 15 */
ClientAPI cli_leak("1234567891011121", "1234567891011121");
return 0;
}
==17060== HEAP SUMMARY:
==17060==     in use at exit: 34 bytes in 2 blocks
==17060==   total heap usage: 9 allocs, 7 frees, 72,950 bytes allocated
==17060== 
==17060== Searching for pointers to 2 not-freed blocks
==17060== Checked 111,624 bytes
==17060== 
==17060== 17 bytes in 1 blocks are definitely lost in loss record 1 of 2
==17060==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17060==    by 0x4F6513C: void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==17060==    by 0x1091B3: APIAuthetication::APIAuthetication(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/rogerio/code-studies/curl-examples/weird)
==17060==    by 0x10937C: ClientAPI::ClientAPI(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/rogerio/code-studies/curl-examples/weird)
==17060==    by 0x108FD3: main (in /home/rogerio/code-studies/curl-examples/weird)
==17060== 
==17060== 17 bytes in 1 blocks are definitely lost in loss record 2 of 2
==17060==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17060==    by 0x4F6513C: void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==17060==    by 0x1091CA: APIAuthetication::APIAuthetication(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/rogerio/code-studies/curl-examples/weird)
==17060==    by 0x10937C: ClientAPI::ClientAPI(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/rogerio/code-studies/curl-examples/weird)
==17060==    by 0x108FD3: main (in /home/rogerio/code-studies/curl-examples/weird)
==17060== 
==17060== LEAK SUMMARY:
==17060==    definitely lost: 34 bytes in 2 blocks
==17060==    indirectly lost: 0 bytes in 0 blocks
==17060==      possibly lost: 0 bytes in 0 blocks
==17060==    still reachable: 0 bytes in 0 blocks
==17060==         suppressed: 0 bytes in 0 blocks
==17060== 
==17060== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==17060== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

如果字符串大小<15:

==17211== 
==17211== HEAP SUMMARY:
==17211==     in use at exit: 0 bytes in 0 blocks
==17211==   total heap usage: 2 allocs, 2 frees, 72,776 bytes allocated
==17211== 
==17211== All heap blocks were freed -- no leaks are possible
==17211== 
==17211== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==17211== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

我知道,如果一个字符串小于16字节,它就不会分配内存,而是使用char缓冲区(优化(,但这种内存泄漏对我来说毫无意义。有人知道我的代码出了什么问题吗?

您已经知道为什么泄漏的大小取决于每个字符串的内容(短字符串优化与动态分配(。

泄漏本身是因为AuthBase没有虚拟析构函数。

Client中的std::unique_ptr成员被销毁时,它将在它所持有的AuthBase*指针上调用delete。由于AuthBase的(默认生成的(析构函数不是虚拟的,APIAuthetication的析构函数将不会被调用,因此它的2个std::string成员不会被破坏,从而泄漏它们为自己分配的任何内存。

您需要向AuthBase:添加一个虚拟析构函数

class AuthBase {
public:
virtual ~AuthBase() = default;
...
};

然后将APIAuthetication的析构函数更改为override,即析构函数:

class APIAuthetication : public AuthBase {
public:
...
~APIAuthetication() override = default;
...
};

ClientBase类及其派生类也是如此。

class ClientBase {
public:
virtual ~ClientBase() = default;
...
};
class Client : public ClientBase {
public:
...
~Client() override = default;
...
};
class ClientAPI : public Client {
public:
...
~ClientAPI() override = default;
};

经验法则:如果一个类可以从派生,那么它应该声明(或继承(一个虚拟析构函数。


附带说明一下,Client的构造函数将原始AuthBase*指针作为输入不是一个好主意。应该使用std::unique_ptr<AuthBase>。在现代C++编码中,最好尽可能避免直接调用new/delete。可以说,让聪明的指针从摇篮到坟墓做好自己的工作。

class Client : public ClientBase {
public:
Client(std::unique_ptr<AuthBase> auth) : m_auth(std::move(auth)) {}
...
private:
std::unique_ptr<AuthBase> m_auth;
};
class ClientAPI : public Client {
public:
ClientAPI(const std::string &api_key, const std::string &api_secret) : 
Client(std::make_unique<APIAuthetication>(api_key, api_secret)) {}
...
};

相关内容

最新更新