如何在虚拟 class 中使用字符串指针避免内存泄漏
How to avoid memory leak with string pointer in virtual class
我正在编写一个基类 class,它包含一个指向字符串的指针,现在我正在尝试编写一个派生的 class,并使用一个赋值运算符将其替换为新值.对于这个特定项目,我需要将字符串存储为指针。
起初,我尝试使用new
,但编译失败。
kolo& operator=(const kolo& ref){
if(this!=&ref){
delete kolor;
kolor=new string(ref.kolor);
r=ref.r;
}
return *this;
}
我已将其更改为使用成功编译的赋值运算符。但是,valgrind 现在报告内存泄漏以及其他问题。
kolo& operator=(const kolo& ref){
if(this!=&ref){
delete kolor;
*kolor=*ref.kolor;
r=ref.r;
}
return *this;
}
为什么 valgrind 报告我的赋值运算符发生内存泄漏?
我的完整代码(波兰语):
class figura {
protected:
string *kolor;
public:
figura() : kolor(new string("nic")) {}
figura(const string& a1) : kolor(new string(a1)) {}
virtual double pole() const = 0;
virtual void wypisz(ostream& out) const {
out<<*kolor;
}
friend ostream& operator<<(ostream& out,const figura& r);
virtual ~figura() { delete kolor; }
};
class kolo: public figura {
protected:
unsigned r;
public:
kolo(): figura(), r(0) {}
kolo(const string& a1, const unsigned a2) :figura(a1), r(a2) {}
kolo(const kolo& ref) : figura(ref), r(ref.r) {}
kolo& operator=(const kolo& ref) {
if(this!=&ref) {
delete kolor;
*kolor=*ref.kolor;
r=ref.r;
}
return *this;
}
double pole()const{
return 3.14*r*r;
}
void wypisz(ostream& out)const{
out<<"Kolor: "<<*kolor<<" "<<"Skladowe: "<<r<<endl;
}
friend ostream& operator<<(ostream& out,const kolo& r);
};
而 valgrind 输出:
==1774== Invalid read of size 8
==1774== at 0x49ABE34: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1774== by 0x10AB7F: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A7B4: main (test.cpp:84)
==1774== Address 0x4dc1c80 is 0 bytes inside a block of size 32 free'd
==1774== at 0x483C1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AB8C: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10B175: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A78B: main (test.cpp:102)
==1774== Block was alloc'd at
==1774== at 0x483AE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AAD8: figura::figura(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (test.cpp:8)
==1774== by 0x10AC27: kolo::kolo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) (test.cpp:25)
==1774== by 0x10A467: main (test.cpp:84)
==1774==
==1774== Invalid free() / delete / delete[] / realloc()
==1774== at 0x483C1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AB8C: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A7B4: main (test.cpp:84)
==1774== Address 0x4dc1c80 is 0 bytes inside a block of size 32 free'd
==1774== at 0x483C1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AB8C: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10B175: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A78B: main (test.cpp:102)
==1774== Block was alloc'd at
==1774== at 0x483AE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AAD8: figura::figura(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (test.cpp:8)
==1774== by 0x10AC27: kolo::kolo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) (test.cpp:25)
==1774== by 0x10A467: main (test.cpp:84)
==1774==
==1774==
==1774== HEAP SUMMARY:
==1774== in use at exit: 32 bytes in 1 blocks
==1774== total heap usage: 14 allocs, 14 frees, 74,088 bytes allocated
==1774==
==1774== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1774== at 0x483AE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AA29: figura::figura() (test.cpp:7)
==1774== by 0x10ABE1: kolo::kolo() (test.cpp:24)
==1774== by 0x10A561: main (test.cpp:88)
==1774==
==1774== LEAK SUMMARY:
==1774== definitely lost: 32 bytes in 1 blocks
==1774== indirectly lost: 0 bytes in 0 blocks
==1774== possibly lost: 0 bytes in 0 blocks
==1774== still reachable: 0 bytes in 0 blocks
==1774== suppressed: 0 bytes in 0 blocks
==1774==
==1774== For lists of detected and suppressed errors, rerun with: -s
==1774== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
去掉指针,所以有0的规则:)
class figura
{
protected:
string kolor;
public:
figura() : kolor("nic") {}
/*explicit*/ figura(const string& s) : kolor(s){}
virtual ~figura() = default;
virtual double pole()const = 0;
virtual void wypisz(ostream& out)const { out<< kolor; }
friend ostream& operator<<(ostream& out,const figura& r);
};
class kolo:public figura
{
protected:
unsigned r = 0;
public:
kolo() : figura(), r(0){}
kolo(const string& s,const unsigned r):figura(s), r(r){}
kolo(const kolo& ref) = default;
kolo& operator=(const kolo& ref) = default;
double pole()const override { return 3.14*r*r; }
void wypisz(ostream& out)const override {
std::out << "Kolor: " << kolor << " " << "Skladowe: " << r << std::endl;
}
friend ostream& operator<<(ostream& out,const kolo& r);
};
rule of three 说如果你实现析构函数、复制赋值或复制构造函数中的任何一个,你需要实现所有这三个。您为 kolo
完成了此操作,但没有为 figurea
在您的代码中的某处,kolo
正在被复制构造(例如在向量中),并使用 figurea
s 复制构造函数的默认版本。这将简单地复制 指针 。现在,原来的字符串已经丢失,这就是泄漏的原因。新字符串被两个不同的对象指向,因此将被销毁两次。第二个销毁是导致无效读取和删除的原因。
有多种方法可以实现复制赋值和复制构造函数,而无需重复代码。例如,尝试 this question。
我还希望,因为 figurea
是实际上有 kolor
的那个,所以 figurea
的复制分配应该负责它,而不是kolor
。这样,如果您从 figurea
派生另一个 class,则不需要重新实现 kolor
复制。
听起来您需要使用原始指针,但通常编写内存管理的最佳方式是不编写内存管理。直接使用字符串,以及字符串的内置内存管理,会使事情变得容易得多。或者,使用智能指针,如 shared_ptr
允许您自动进行复制和删除。
我正在编写一个基类 class,它包含一个指向字符串的指针,现在我正在尝试编写一个派生的 class,并使用一个赋值运算符将其替换为新值.对于这个特定项目,我需要将字符串存储为指针。
起初,我尝试使用new
,但编译失败。
kolo& operator=(const kolo& ref){
if(this!=&ref){
delete kolor;
kolor=new string(ref.kolor);
r=ref.r;
}
return *this;
}
我已将其更改为使用成功编译的赋值运算符。但是,valgrind 现在报告内存泄漏以及其他问题。
kolo& operator=(const kolo& ref){
if(this!=&ref){
delete kolor;
*kolor=*ref.kolor;
r=ref.r;
}
return *this;
}
为什么 valgrind 报告我的赋值运算符发生内存泄漏?
我的完整代码(波兰语):
class figura {
protected:
string *kolor;
public:
figura() : kolor(new string("nic")) {}
figura(const string& a1) : kolor(new string(a1)) {}
virtual double pole() const = 0;
virtual void wypisz(ostream& out) const {
out<<*kolor;
}
friend ostream& operator<<(ostream& out,const figura& r);
virtual ~figura() { delete kolor; }
};
class kolo: public figura {
protected:
unsigned r;
public:
kolo(): figura(), r(0) {}
kolo(const string& a1, const unsigned a2) :figura(a1), r(a2) {}
kolo(const kolo& ref) : figura(ref), r(ref.r) {}
kolo& operator=(const kolo& ref) {
if(this!=&ref) {
delete kolor;
*kolor=*ref.kolor;
r=ref.r;
}
return *this;
}
double pole()const{
return 3.14*r*r;
}
void wypisz(ostream& out)const{
out<<"Kolor: "<<*kolor<<" "<<"Skladowe: "<<r<<endl;
}
friend ostream& operator<<(ostream& out,const kolo& r);
};
而 valgrind 输出:
==1774== Invalid read of size 8
==1774== at 0x49ABE34: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1774== by 0x10AB7F: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A7B4: main (test.cpp:84)
==1774== Address 0x4dc1c80 is 0 bytes inside a block of size 32 free'd
==1774== at 0x483C1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AB8C: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10B175: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A78B: main (test.cpp:102)
==1774== Block was alloc'd at
==1774== at 0x483AE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AAD8: figura::figura(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (test.cpp:8)
==1774== by 0x10AC27: kolo::kolo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) (test.cpp:25)
==1774== by 0x10A467: main (test.cpp:84)
==1774==
==1774== Invalid free() / delete / delete[] / realloc()
==1774== at 0x483C1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AB8C: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A7B4: main (test.cpp:84)
==1774== Address 0x4dc1c80 is 0 bytes inside a block of size 32 free'd
==1774== at 0x483C1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AB8C: figura::~figura() (test.cpp:14)
==1774== by 0x10B155: kolo::~kolo() (test.cpp:20)
==1774== by 0x10B175: kolo::~kolo() (test.cpp:20)
==1774== by 0x10A78B: main (test.cpp:102)
==1774== Block was alloc'd at
==1774== at 0x483AE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AAD8: figura::figura(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (test.cpp:8)
==1774== by 0x10AC27: kolo::kolo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) (test.cpp:25)
==1774== by 0x10A467: main (test.cpp:84)
==1774==
==1774==
==1774== HEAP SUMMARY:
==1774== in use at exit: 32 bytes in 1 blocks
==1774== total heap usage: 14 allocs, 14 frees, 74,088 bytes allocated
==1774==
==1774== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1774== at 0x483AE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==1774== by 0x10AA29: figura::figura() (test.cpp:7)
==1774== by 0x10ABE1: kolo::kolo() (test.cpp:24)
==1774== by 0x10A561: main (test.cpp:88)
==1774==
==1774== LEAK SUMMARY:
==1774== definitely lost: 32 bytes in 1 blocks
==1774== indirectly lost: 0 bytes in 0 blocks
==1774== possibly lost: 0 bytes in 0 blocks
==1774== still reachable: 0 bytes in 0 blocks
==1774== suppressed: 0 bytes in 0 blocks
==1774==
==1774== For lists of detected and suppressed errors, rerun with: -s
==1774== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
去掉指针,所以有0的规则:)
class figura
{
protected:
string kolor;
public:
figura() : kolor("nic") {}
/*explicit*/ figura(const string& s) : kolor(s){}
virtual ~figura() = default;
virtual double pole()const = 0;
virtual void wypisz(ostream& out)const { out<< kolor; }
friend ostream& operator<<(ostream& out,const figura& r);
};
class kolo:public figura
{
protected:
unsigned r = 0;
public:
kolo() : figura(), r(0){}
kolo(const string& s,const unsigned r):figura(s), r(r){}
kolo(const kolo& ref) = default;
kolo& operator=(const kolo& ref) = default;
double pole()const override { return 3.14*r*r; }
void wypisz(ostream& out)const override {
std::out << "Kolor: " << kolor << " " << "Skladowe: " << r << std::endl;
}
friend ostream& operator<<(ostream& out,const kolo& r);
};
rule of three 说如果你实现析构函数、复制赋值或复制构造函数中的任何一个,你需要实现所有这三个。您为 kolo
完成了此操作,但没有为 figurea
在您的代码中的某处,kolo
正在被复制构造(例如在向量中),并使用 figurea
s 复制构造函数的默认版本。这将简单地复制 指针 。现在,原来的字符串已经丢失,这就是泄漏的原因。新字符串被两个不同的对象指向,因此将被销毁两次。第二个销毁是导致无效读取和删除的原因。
有多种方法可以实现复制赋值和复制构造函数,而无需重复代码。例如,尝试 this question。
我还希望,因为 figurea
是实际上有 kolor
的那个,所以 figurea
的复制分配应该负责它,而不是kolor
。这样,如果您从 figurea
派生另一个 class,则不需要重新实现 kolor
复制。
听起来您需要使用原始指针,但通常编写内存管理的最佳方式是不编写内存管理。直接使用字符串,以及字符串的内置内存管理,会使事情变得容易得多。或者,使用智能指针,如 shared_ptr
允许您自动进行复制和删除。