Valgrind在使用字符串类型成员时识别内存泄漏(使用nvcc编译)
-
21-12-2019 - |
题
我不确定这是否是一个 漏洞 或者 不是, ,但是当我在结构或类中使用字符串类型成员时,valgrind 会识别内存泄漏。我尝试根据自己的应用程序构建一个简单的代码,如果它仍然很大,我很抱歉......
// ====================================================================
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
// ====================================================================
string int2str(const int &i) {
return static_cast<ostringstream*>(
&(ostringstream() << i))->str();
}
// ====================================================================
class P;
// ====================================================================
struct Node {
virtual char isType() const = 0;
};
// ====================================================================
struct X : Node {
string st;
int id;
X(const string &_st, const int &_id);
char isType() const { return 'x'; };
// Those member functions are after class P declaration:
P use_as_P();
P use_as_P(const P &arg0);
P use_as_P(const P &arg0, const P &arg1);
};
X::X(const string &_st, const int &_id) : st(_st), id(_id) { }
// ====================================================================
class P {
friend struct X;
private:
Node *node;
vector<P> children;
public:
P() : node(NULL) {};
P(const P &source);
void swap(P &other);
string print_this();
~P();
};
P::P(const P &source) {
this->children = source.children;
switch(source.node->isType()) {
case 'x':
this->node = new X(static_cast<X*>(source.node)->st,
static_cast<X*>(source.node)->id);
break;
}
}
void P::swap(P &other) {
std::swap(this->node, other.node);
std::swap(this->children, other.children);
}
string P::print_this() {
string msg = "( ";
msg += static_cast<X*>(this->node)->st;
msg += int2str(static_cast<X*>(this->node)->id);
msg += " ";
for(size_t i = 0; i < this->children.size(); i++)
msg += children.at(i).print_this();
msg += ") ";
return msg;
}
P::~P() {
if(this->node != NULL)
delete node;
this->children.clear();
}
// ====================================================================
P X::use_as_P() {
P ast_aux;
ast_aux.node = new X(this->st,this->id);
return ast_aux;
}
P X::use_as_P(const P &arg0) {
P ast_aux;
ast_aux.node = new X(this->st,this->id);
ast_aux.children.push_back(arg0);
return ast_aux;
}
P X::use_as_P(const P &arg0, const P &arg1) {
P ast_aux;
ast_aux.node = new X(this->st,this->id);
ast_aux.children.push_back(arg0);
ast_aux.children.push_back(arg1);
return ast_aux;
}
// ====================================================================
// ** MAIN **
// ====================================================================
int main(int argc, char **argv)
{
X a("how",0), b("what",1), c("why",2), d("when",3);
P testing = a.use_as_P(b.use_as_P(c.use_as_P()),d.use_as_P());
cout << testing.print_this() << endl;
return 0;
}
// ====================================================================
编译:
nvcc -arch sm_20 -o LEAK_test_with_string LEAK_test_with_string.cu
valgrind 的分析如下:
==5877== Memcheck, a memory error detector
==5877== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==5877== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==5877== Command: ./LEAK_test_with_string
==5877==
( how0 ( what1 ( why2 ) ) ( when3 ) )
==5877==
==5877== HEAP SUMMARY:
==5877== in use at exit: 114 bytes in 4 blocks
==5877== total heap usage: 47 allocs, 43 frees, 3,701 bytes allocated
==5877==
==5877== 28 bytes in 1 blocks are definitely lost in loss record 1 of 4
==5877== at 0x4C2A879: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5877== by 0x5516F38: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518640: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518A57: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x403691: main (in /home/igor/projects/system_modeling/LEAK_test_with_string)
==5877== : st(_st), id(_id) {}
==5877== 28 bytes in 1 blocks are definitely lost in loss record 2 of 4
==5877== at 0x4C2A879: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5877== by 0x5516F38: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518640: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518A57: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x40375A: main (in /home/igor/projects/system_modeling/LEAK_test_with_string)
==5877==
==5877== 29 bytes in 1 blocks are definitely lost in loss record 3 of 4
==5877== at 0x4C2A879: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5877== by 0x5516F38: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518640: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518A57: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x4036F7: main (in /home/igor/projects/system_modeling/LEAK_test_with_string)
==5877==
==5877== 29 bytes in 1 blocks are definitely lost in loss record 4 of 4
==5877== at 0x4C2A879: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5877== by 0x5516F38: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518640: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x5518A57: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==5877== by 0x4037BD: main (in /home/igor/projects/system_modeling/LEAK_test_with_string)
==5877==
==5877== LEAK SUMMARY:
==5877== definitely lost: 114 bytes in 4 blocks
==5877== indirectly lost: 0 bytes in 0 blocks
==5877== possibly lost: 0 bytes in 0 blocks
==5877== still reachable: 0 bytes in 0 blocks
==5877== suppressed: 0 bytes in 0 blocks
==5877==
==5877== For counts of detected and suppressed errors, rerun with: -v
==5877== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 2 from 2)
当我更改 C++ 时,就会出现“这是一个错误”的疑问 细绳 成员变成普通 C 字符* 成员。只需要把上面写的这部分代码改一下即可:
// ====================================================================
struct X : Node {
char st[6]; // <=============== HERE!
int id;
X(const string &_st, const int &_id);
char isType() const { return 'x'; };
// Those member functions are after class P declaration:
P use_as_P();
P use_as_P(const P &arg0);
P use_as_P(const P &arg0, const P &arg1);
};
X::X(const string &_st, const int &_id) : id(_id) { // <=============== HERE!
strcpy(st, _st.c_str()); // <=============== HERE!
}
// ====================================================================
请注意,我仍然使用 细绳 在我的代码中,但现在没有 细绳 成员宣布。这样,valgrind 就不会再抱怨了:
==5977== Memcheck, a memory error detector
==5977== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==5977== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==5977== Command: ./LEAK_test_without_string
==5977==
( how0 ( what1 ( why2 ) ) ( when3 ) )
==5977==
==5977== HEAP SUMMARY:
==5977== in use at exit: 0 bytes in 0 blocks
==5977== total heap usage: 57 allocs, 57 frees, 3,986 bytes allocated
==5977==
==5977== All heap blocks were freed -- no leaks are possible
==5977==
==5977== For counts of detected and suppressed errors, rerun with: -v
==5977== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
有人对此有见解吗?我的意思是,这是一个应该报告的错误,还是我的字符串版本代码中实际上缺少某些内容?
解决方案
既然没有人咬紧牙关,我将扩展我的评论:你的 Node
类没有虚拟析构函数,这使得以下行
delete node;
调用未定义的行为 - 这只会调用析构函数 Node
, ,析构函数为 X
从未被调用过。
解决方案很简单,提供一个虚拟析构函数 Node
:
struct Node {
virtual char isType() const = 0;
virtual ~Node() =default;
// or virtual ~Node(){} if your compiler does not support defaulted functions.
}
您现在可以安全地 delete
的基指针 Node
指向继承的类。
使用内存泄漏的原因 std::string
是因为当你删除基指针时 node
, ,析构函数为 X
没有被调用,所以析构函数也没有被调用 st
. 。这 char
数组版本不会泄漏,因为 node
指向一个简单的内存块,并且 X
在这种情况下没有任何需要销毁的复杂成员,所以 delete node
幸运的是释放了分配给的所有内存 X
及其成员。请记住,这是 未定义的行为 然而,编译器可以合法地做任何它想做的事。
如果您的基类只有一个 virtual
功能, 总是 添加虚拟析构函数。如果您绝对确定不会使用指向基类的指针删除继承的类,则可以省略虚拟析构函数,但无论如何添加一个更安全,以防万一(gcc 编译器标志 -Weffc++
也会告诉你这么多)。