我不确定这是否是一个 漏洞 或者 不是, ,但是当我在结构或类中使用字符串类型成员时,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++ 也会告诉你这么多)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top