Valgrind 声称内存释放中有太多释放
Valgrind claims there are too many frees in memory deallocation
我正在阅读 Stroustrup "the c++ programming language 4th edition" 并读到他谈论析构函数的部分。
我尝试按照 Stroustrup 示例并检查 valgrind 是否满意,但事实并非如此:它声称存在一些错误。
我不太明白它的评论,或者问题可能在哪里。
我所理解的是 valgrind 认为有额外的空闲,我没有检测到。
在此非常感谢专家的帮助。谢谢
请注意,我在这个特定示例中使用了 try catch,但我认为这不是问题所在...这段代码中真正重要的可能是构造函数和析构函数。但是,为了完整性,我 post 整件事
#include <iostream>
using namespace std;
class Vector
{
public:
Vector();
Vector(int s);
~Vector();
double &operator[](int i);
int size();
static int get_default_size();
private:
double *elem;
int sz;
static const int default_size = 5;
};
Vector::Vector()
{
elem = new double[default_size];
sz = default_size;
for (int i = 0; i < sz; ++i)
{ //initialize elem array's values
elem[i] = i;
}
}
Vector::Vector(int s)
//: elem{new double[s]}, sz{s} //a dangerous idea - what if s is negative?
{
if (s < 0)
throw length_error{""};
elem = new double[s];
sz = s;
for (int i = 0; i < sz; ++i)
{ //initialize elem array's values
elem[i] = 0;
}
}
Vector::~Vector()
{
cout << "destructor working now" << endl;
delete[] elem;
}
double &Vector::operator[](int i)
{
return elem[i];
}
int Vector::size()
{
return sz;
}
int Vector::get_default_size()
{
return default_size;
}
Vector test()
{
try
{
Vector v(-27);
return v;
}
catch (std::length_error)
{
cout << "negative length" << endl;
cout << "length will get default size, " << Vector::get_default_size() << endl;
Vector v = Vector();
return v;
}
}
int main()
{
Vector v = test();
for (int i = 0; i < v.size(); ++i)
cout << v[i] << endl;
}
我希望 valgrind 显示 0 个错误,但这是我从中收到的输出:
<pre>==14640== Memcheck, a memory error detector
==14640== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14640== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==14640== Command: ./a.out
==14640==
negative length
length will get default size, 5
destructor working now
==14640== Invalid read of size 8
==14640== at 0x10933D: main (vector_improved1.cpp:83)
==14640== Address 0x5b7e190 is 0 bytes inside a block of size 40 free'd
==14640== at 0x4C3173B: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x10913A: Vector::~Vector() (vector_improved1.cpp:45)
==14640== by 0x109299: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640== Block was alloc'd at
==14640== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x108FBF: Vector::Vector() (vector_improved1.cpp:21)
==14640== by 0x10927A: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640==
0
1
2
3
4
destructor working now
==14640== Invalid free() / delete / delete[] / realloc()
==14640== at 0x4C3173B: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x10913A: Vector::~Vector() (vector_improved1.cpp:45)
==14640== by 0x10937B: main (vector_improved1.cpp:81)
==14640== Address 0x5b7e190 is 0 bytes inside a block of size 40 free'd
==14640== at 0x4C3173B: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x10913A: Vector::~Vector() (vector_improved1.cpp:45)
==14640== by 0x109299: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640== Block was alloc'd at
==14640== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x108FBF: Vector::Vector() (vector_improved1.cpp:21)
==14640== by 0x10927A: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640==
==14640==
==14640== HEAP SUMMARY:
==14640== in use at exit: 0 bytes in 0 blocks
==14640== total heap usage: 4 allocs, 5 frees, 73,912 bytes allocated
==14640==
==14640== All heap blocks were freed -- no leaks are possible
==14640==
==14640== For counts of detected and suppressed errors, rerun with: -v
==14640== ERROR SUMMARY: 6 errors from 2 contexts (suppressed: 0 from 0)
要解释为什么会发生这种情况,请查看您的代码的这个简化版本:
#include <iostream>
class Vector {
public:
Vector() {
std::cout << "Vector constructor (this=" << std::hex << this << ")" << std::endl;
}
Vector(const Vector& other){
std::cout << "Vector copy constructor (other=" << std::hex << (&other) << " this=" << this << ")" << std::endl;
}
Vector& operator=(const Vector& other){
std::cout << "Vector assignment operator (other=" << std::hex << (&other) << " this=" << this << ")" << std::endl;
return (*this);
}
~Vector(){
std::cout << "Vector destructor (this=" << this << ")" << std::endl;
}
};
Vector test() {
Vector o = Vector();
return o;
}
int main() {
Vector o = test();
return 0;
}
及其在运行时的输出:
Vector constructor (this=0x7ffee4a777d0)
Vector copy constructor (other=0x7ffee4a777d0 this=0x7ffee4a777d8)
Vector destructor (this=0x7ffee4a777d0)
Vector copy constructor (other=0x7ffee4a777d8 this=0x7ffee4a77810)
Vector destructor (this=0x7ffee4a777d8)
Vector copy constructor (other=0x7ffee4a77810 this=0x7ffee4a77818)
Vector destructor (this=0x7ffee4a77810)
Vector destructor (this=0x7ffee4a77818)
如您所见,最初在 test()
中创建的向量通过各种实例被复制了几次(每个创建的实例也会被销毁)。每当您的代码中发生这种情况时,都会调用编译器自动生成的默认复制构造函数。
默认复制构造函数只是将每个属性从源实例复制到目标实例(包括double *elem
)。使用动态分配的内存时,语义通常是错误的,因为多个实例然后 'own' 相同的底层数据。当首先创建的向量被销毁时,它释放 elem
,使其他实例指向悬空(无效)内存。
为了解决这个问题,您需要确保您的 vector 实现包括一个赋值和复制构造函数,它们实际分配新的内存,并将基础数据从复制的 vector 复制到目标 vector。
我正在阅读 Stroustrup "the c++ programming language 4th edition" 并读到他谈论析构函数的部分。
我尝试按照 Stroustrup 示例并检查 valgrind 是否满意,但事实并非如此:它声称存在一些错误。
我不太明白它的评论,或者问题可能在哪里。 我所理解的是 valgrind 认为有额外的空闲,我没有检测到。
在此非常感谢专家的帮助。谢谢
请注意,我在这个特定示例中使用了 try catch,但我认为这不是问题所在...这段代码中真正重要的可能是构造函数和析构函数。但是,为了完整性,我 post 整件事
#include <iostream>
using namespace std;
class Vector
{
public:
Vector();
Vector(int s);
~Vector();
double &operator[](int i);
int size();
static int get_default_size();
private:
double *elem;
int sz;
static const int default_size = 5;
};
Vector::Vector()
{
elem = new double[default_size];
sz = default_size;
for (int i = 0; i < sz; ++i)
{ //initialize elem array's values
elem[i] = i;
}
}
Vector::Vector(int s)
//: elem{new double[s]}, sz{s} //a dangerous idea - what if s is negative?
{
if (s < 0)
throw length_error{""};
elem = new double[s];
sz = s;
for (int i = 0; i < sz; ++i)
{ //initialize elem array's values
elem[i] = 0;
}
}
Vector::~Vector()
{
cout << "destructor working now" << endl;
delete[] elem;
}
double &Vector::operator[](int i)
{
return elem[i];
}
int Vector::size()
{
return sz;
}
int Vector::get_default_size()
{
return default_size;
}
Vector test()
{
try
{
Vector v(-27);
return v;
}
catch (std::length_error)
{
cout << "negative length" << endl;
cout << "length will get default size, " << Vector::get_default_size() << endl;
Vector v = Vector();
return v;
}
}
int main()
{
Vector v = test();
for (int i = 0; i < v.size(); ++i)
cout << v[i] << endl;
}
我希望 valgrind 显示 0 个错误,但这是我从中收到的输出:
<pre>==14640== Memcheck, a memory error detector
==14640== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14640== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==14640== Command: ./a.out
==14640==
negative length
length will get default size, 5
destructor working now
==14640== Invalid read of size 8
==14640== at 0x10933D: main (vector_improved1.cpp:83)
==14640== Address 0x5b7e190 is 0 bytes inside a block of size 40 free'd
==14640== at 0x4C3173B: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x10913A: Vector::~Vector() (vector_improved1.cpp:45)
==14640== by 0x109299: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640== Block was alloc'd at
==14640== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x108FBF: Vector::Vector() (vector_improved1.cpp:21)
==14640== by 0x10927A: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640==
0
1
2
3
4
destructor working now
==14640== Invalid free() / delete / delete[] / realloc()
==14640== at 0x4C3173B: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x10913A: Vector::~Vector() (vector_improved1.cpp:45)
==14640== by 0x10937B: main (vector_improved1.cpp:81)
==14640== Address 0x5b7e190 is 0 bytes inside a block of size 40 free'd
==14640== at 0x4C3173B: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x10913A: Vector::~Vector() (vector_improved1.cpp:45)
==14640== by 0x109299: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640== Block was alloc'd at
==14640== at 0x4C3089F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14640== by 0x108FBF: Vector::Vector() (vector_improved1.cpp:21)
==14640== by 0x10927A: test() (vector_improved1.cpp:74)
==14640== by 0x10930E: main (vector_improved1.cpp:81)
==14640==
==14640==
==14640== HEAP SUMMARY:
==14640== in use at exit: 0 bytes in 0 blocks
==14640== total heap usage: 4 allocs, 5 frees, 73,912 bytes allocated
==14640==
==14640== All heap blocks were freed -- no leaks are possible
==14640==
==14640== For counts of detected and suppressed errors, rerun with: -v
==14640== ERROR SUMMARY: 6 errors from 2 contexts (suppressed: 0 from 0)
要解释为什么会发生这种情况,请查看您的代码的这个简化版本:
#include <iostream>
class Vector {
public:
Vector() {
std::cout << "Vector constructor (this=" << std::hex << this << ")" << std::endl;
}
Vector(const Vector& other){
std::cout << "Vector copy constructor (other=" << std::hex << (&other) << " this=" << this << ")" << std::endl;
}
Vector& operator=(const Vector& other){
std::cout << "Vector assignment operator (other=" << std::hex << (&other) << " this=" << this << ")" << std::endl;
return (*this);
}
~Vector(){
std::cout << "Vector destructor (this=" << this << ")" << std::endl;
}
};
Vector test() {
Vector o = Vector();
return o;
}
int main() {
Vector o = test();
return 0;
}
及其在运行时的输出:
Vector constructor (this=0x7ffee4a777d0)
Vector copy constructor (other=0x7ffee4a777d0 this=0x7ffee4a777d8)
Vector destructor (this=0x7ffee4a777d0)
Vector copy constructor (other=0x7ffee4a777d8 this=0x7ffee4a77810)
Vector destructor (this=0x7ffee4a777d8)
Vector copy constructor (other=0x7ffee4a77810 this=0x7ffee4a77818)
Vector destructor (this=0x7ffee4a77810)
Vector destructor (this=0x7ffee4a77818)
如您所见,最初在 test()
中创建的向量通过各种实例被复制了几次(每个创建的实例也会被销毁)。每当您的代码中发生这种情况时,都会调用编译器自动生成的默认复制构造函数。
默认复制构造函数只是将每个属性从源实例复制到目标实例(包括double *elem
)。使用动态分配的内存时,语义通常是错误的,因为多个实例然后 'own' 相同的底层数据。当首先创建的向量被销毁时,它释放 elem
,使其他实例指向悬空(无效)内存。
为了解决这个问题,您需要确保您的 vector 实现包括一个赋值和复制构造函数,它们实际分配新的内存,并将基础数据从复制的 vector 复制到目标 vector。