Valgrind 发现了 3 个内存泄漏,但我不知道它们在哪里
Valgrind found 3 memory leaks but I can't figure out where they are
我尝试自己实现基本字符串 class,它工作正常,但 Valgrind 说有 3 处内存泄漏,我无法弄清楚泄漏的位置和原因。我真的试图删除不再使用的所有内容(我今天开始使用 Valgrind)。
现在我真的很关心我的基本 C/C++ 内存管理知识。我对代码中 Valgrind 发现泄漏的地方进行了评论 (//VALGRIND)。我还上传了此错误消息的屏幕截图
click to see the screenshot.
编辑:我更新了截图,所以你可以看到完整的输出。
StringT.h
template<typename char_type = char>
class StringT {
public:
explicit StringT(const char_type *str) {
if (str != nullptr) {
size_t len = strlen(str);
m_str = new char_type[len + 1]; //VALGRIND: 6 bytes in 1 blocks are definitely lost in loss record 1 of 3
strcpy(m_str, str);
}
}
~StringT() {
delete [] m_str;
}
StringT(const StringT & other) {
size_t len = 0;
if (other.m_str) len = strlen(other.m_str);
m_str = new char_type[len + 1]; //VALGRIND: 6 bytes in 1 blocks are definitely lost in loss record 2 of 3
strcpy(m_str, other.m_str);
}
StringT(StringT && other) noexcept {
m_str = other.m_str;
other.m_str = nullptr;
}
StringT & operator+=(const StringT &other) {
if (other.m_str == nullptr) //when other str is empty just return current Str
return *this;
const size_t mysize{m_str ? strlen(m_str) : 0}; // check if not null then call strlen
const size_t osize{other.m_str ? strlen(other.m_str) : 0};
char *newStr = new char_type[osize + mysize + 1]; //VALGRIND: 11 bytes in 1 blocks are definitely lost in loss record 3 of 3
newStr[0] = '[=10=]'; //strcat searches for '[=10=]', so newStr has to be a valid String
if (m_str) strcat(newStr, m_str);
if (other.m_str) strcat(newStr, other.m_str);
delete[] m_str; //delete old string
m_str = newStr; //set member to new concatenated str
return *this;
}
size_t length() const {
if (!m_str) return 0;
return strlen(m_str);
}
friend
std::ostream &operator<<(std::ostream &out, StringT<> &other) {
if (other.m_str) out << other.m_str;
return out;
}
private:
char_type *m_str{nullptr};
};
main.cpp
int main() {
const char *cArr = "Hello";
const char *cArr2 = "World";
StringT<char> hello(cArr);
StringT<char> world(cArr2);
StringT<char> emptyStr;
std::cout << "hello: " << hello << std::endl;
std::cout << "world: " << world << std::endl;
std::cout << "emptyStr: " << emptyStr << std::endl;
StringT<char> hCopy(hello);
StringT<char> wMove(std::move(world));
std::cout << "hCopy: " << hello << std::endl;
std::cout << "hCopy: " << hCopy << std::endl;
std::cout << "world: " << world << std::endl;
std::cout<< "wMove: " << wMove << std::endl;
std::cout<< "lenMove: " << wMove.length() << std::endl;
std::cout<< "lenEmptyStr: " << emptyStr.length() << std::endl;
hello += wMove;
std::cout<< "hello += world: " << hello << std::endl;
return 0;
}
您的删除在这里:
StringT() {
delete [] m_str;
}
但那是构造函数,不是析构函数。
正如已经回答的那样,delete
需要在析构函数中。但是,解决您的问题的正确方法是在这种情况下不要手动进行内存管理。您应该为您的 m_str
成员使用 std::unique_ptr
:
std::unique_ptr<char_type[]> m_str;
这使您不必手动 new
和 delete
。这也有助于在出现异常时防止内存泄漏。即使您 delete
分配了所有内容,如果在 new
和 delete
之间发生异常,您仍然会发生内存泄漏。 unique_ptr
有助于防止此类泄漏。
您的 class 只需稍作改动:
template<typename char_type = char>
class StringT {
public:
StringT()
{}
explicit StringT(const char_type *str)
{
if (str != nullptr) {
size_t len = strlen(str);
m_str = std::make_unique<char_type[]>(len + 1);
strcpy(m_str.get(), str);
}
}
StringT(const StringT & other)
{
size_t len = 0;
if (other.m_str)
len = strlen(other.m_str.get());
m_str = std::make_unique<char_type[]>(len + 1);
strcpy(m_str.get(), other.m_str.get());
}
StringT(StringT && other) noexcept
{
m_str = std::move(other.m_str);
}
StringT & operator+=(const StringT &other)
{
if (other.m_str == nullptr)
return *this;
const size_t mysize{m_str ? strlen(m_str.get()) : 0};
const size_t osize{strlen(other.m_str.get())};
auto newStr = std::make_unique<char_type[]>(osize + mysize + 1);
newStr[0] = '[=11=]';
if (m_str)
strcat(newStr.get(), m_str.get());
strcat(newStr.get(), other.m_str.get());
m_str = std::move(newStr);
return *this;
}
size_t length() const
{
if (!m_str)
return 0;
return strlen(m_str.get());
}
friend
std::ostream &operator<<(std::ostream &out, StringT<> &other)
{
if (other.m_str)
out << other.m_str.get();
return out;
}
private:
std::unique_ptr<char_type[]> m_str;
};
您会注意到此代码中没有对 new
或 delete
的调用。 m_str
将在需要时自行删除分配的内存。
我尝试自己实现基本字符串 class,它工作正常,但 Valgrind 说有 3 处内存泄漏,我无法弄清楚泄漏的位置和原因。我真的试图删除不再使用的所有内容(我今天开始使用 Valgrind)。 现在我真的很关心我的基本 C/C++ 内存管理知识。我对代码中 Valgrind 发现泄漏的地方进行了评论 (//VALGRIND)。我还上传了此错误消息的屏幕截图 click to see the screenshot.
编辑:我更新了截图,所以你可以看到完整的输出。
StringT.h
template<typename char_type = char>
class StringT {
public:
explicit StringT(const char_type *str) {
if (str != nullptr) {
size_t len = strlen(str);
m_str = new char_type[len + 1]; //VALGRIND: 6 bytes in 1 blocks are definitely lost in loss record 1 of 3
strcpy(m_str, str);
}
}
~StringT() {
delete [] m_str;
}
StringT(const StringT & other) {
size_t len = 0;
if (other.m_str) len = strlen(other.m_str);
m_str = new char_type[len + 1]; //VALGRIND: 6 bytes in 1 blocks are definitely lost in loss record 2 of 3
strcpy(m_str, other.m_str);
}
StringT(StringT && other) noexcept {
m_str = other.m_str;
other.m_str = nullptr;
}
StringT & operator+=(const StringT &other) {
if (other.m_str == nullptr) //when other str is empty just return current Str
return *this;
const size_t mysize{m_str ? strlen(m_str) : 0}; // check if not null then call strlen
const size_t osize{other.m_str ? strlen(other.m_str) : 0};
char *newStr = new char_type[osize + mysize + 1]; //VALGRIND: 11 bytes in 1 blocks are definitely lost in loss record 3 of 3
newStr[0] = '[=10=]'; //strcat searches for '[=10=]', so newStr has to be a valid String
if (m_str) strcat(newStr, m_str);
if (other.m_str) strcat(newStr, other.m_str);
delete[] m_str; //delete old string
m_str = newStr; //set member to new concatenated str
return *this;
}
size_t length() const {
if (!m_str) return 0;
return strlen(m_str);
}
friend
std::ostream &operator<<(std::ostream &out, StringT<> &other) {
if (other.m_str) out << other.m_str;
return out;
}
private:
char_type *m_str{nullptr};
};
main.cpp
int main() {
const char *cArr = "Hello";
const char *cArr2 = "World";
StringT<char> hello(cArr);
StringT<char> world(cArr2);
StringT<char> emptyStr;
std::cout << "hello: " << hello << std::endl;
std::cout << "world: " << world << std::endl;
std::cout << "emptyStr: " << emptyStr << std::endl;
StringT<char> hCopy(hello);
StringT<char> wMove(std::move(world));
std::cout << "hCopy: " << hello << std::endl;
std::cout << "hCopy: " << hCopy << std::endl;
std::cout << "world: " << world << std::endl;
std::cout<< "wMove: " << wMove << std::endl;
std::cout<< "lenMove: " << wMove.length() << std::endl;
std::cout<< "lenEmptyStr: " << emptyStr.length() << std::endl;
hello += wMove;
std::cout<< "hello += world: " << hello << std::endl;
return 0;
}
您的删除在这里:
StringT() {
delete [] m_str;
}
但那是构造函数,不是析构函数。
正如已经回答的那样,delete
需要在析构函数中。但是,解决您的问题的正确方法是在这种情况下不要手动进行内存管理。您应该为您的 m_str
成员使用 std::unique_ptr
:
std::unique_ptr<char_type[]> m_str;
这使您不必手动 new
和 delete
。这也有助于在出现异常时防止内存泄漏。即使您 delete
分配了所有内容,如果在 new
和 delete
之间发生异常,您仍然会发生内存泄漏。 unique_ptr
有助于防止此类泄漏。
您的 class 只需稍作改动:
template<typename char_type = char>
class StringT {
public:
StringT()
{}
explicit StringT(const char_type *str)
{
if (str != nullptr) {
size_t len = strlen(str);
m_str = std::make_unique<char_type[]>(len + 1);
strcpy(m_str.get(), str);
}
}
StringT(const StringT & other)
{
size_t len = 0;
if (other.m_str)
len = strlen(other.m_str.get());
m_str = std::make_unique<char_type[]>(len + 1);
strcpy(m_str.get(), other.m_str.get());
}
StringT(StringT && other) noexcept
{
m_str = std::move(other.m_str);
}
StringT & operator+=(const StringT &other)
{
if (other.m_str == nullptr)
return *this;
const size_t mysize{m_str ? strlen(m_str.get()) : 0};
const size_t osize{strlen(other.m_str.get())};
auto newStr = std::make_unique<char_type[]>(osize + mysize + 1);
newStr[0] = '[=11=]';
if (m_str)
strcat(newStr.get(), m_str.get());
strcat(newStr.get(), other.m_str.get());
m_str = std::move(newStr);
return *this;
}
size_t length() const
{
if (!m_str)
return 0;
return strlen(m_str.get());
}
friend
std::ostream &operator<<(std::ostream &out, StringT<> &other)
{
if (other.m_str)
out << other.m_str.get();
return out;
}
private:
std::unique_ptr<char_type[]> m_str;
};
您会注意到此代码中没有对 new
或 delete
的调用。 m_str
将在需要时自行删除分配的内存。