我在 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;
}
Run Code Online (Sandbox Code Playgroud)
==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)
Run Code Online (Sandbox Code Playgroud)
如果字符串大小 < 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)
Run Code Online (Sandbox Code Playgroud)
我知道如果一个字符串小于 16 个字节,它不会分配内存并使用字符缓冲区(优化),但对我来说这种内存泄漏没有意义。有谁知道我的代码有什么问题吗?
您已经知道为什么泄漏的大小取决于每个字符串的内容(短字符串优化与动态分配)。
泄漏本身是因为AuthBase没有虚拟析构函数。
当std::unique_ptrin 中的成员Client被销毁时,它将调用delete它所AuthBase*持有的指针。并且由于AuthBase的(默认生成的)析构函数不是虚拟的,因此APIAuthetication不会调用 的析构函数,因此它的 2 个std::string成员不会被销毁,从而泄漏它们为自己分配的任何内存。
您需要添加一个虚拟析构函数AuthBase:
class AuthBase {
public:
virtual ~AuthBase() = default;
...
};
Run Code Online (Sandbox Code Playgroud)
然后将APIAuthetication的析构函数更改override为该析构函数:
class APIAuthetication : public AuthBase {
public:
...
~APIAuthetication() override = default;
...
};
Run Code Online (Sandbox Code Playgroud)
ClientBase您的类及其派生类也是如此。
class ClientBase {
public:
virtual ~ClientBase() = default;
...
};
class Client : public ClientBase {
public:
...
~Client() override = default;
...
};
class ClientAPI : public Client {
public:
...
~ClientAPI() override = default;
};
Run Code Online (Sandbox Code Playgroud)
经验法则:如果一个类可以派生,它应该声明(或继承)一个虚拟析构函数。
Client附带说明一下,的构造函数采用原始指针作为输入并不是一个好主意AuthBase*。应该用 astd::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)) {}
...
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1373 次 |
| 最近记录: |