字符串类中的字符串(const char *)构造函数内存泄漏
Posted
技术标签:
【中文标题】字符串类中的字符串(const char *)构造函数内存泄漏【英文标题】:String(const char*) constructor memory leak in string class 【发布时间】:2020-04-06 20:00:49 【问题描述】:所以我试图实现 String 类,但我收到一个错误,说分段错误。我认为我的构造函数中存在内存泄漏。你能告诉我我做错了什么吗?谢谢。
这是构造函数代码,我不能使用任何标准库功能。
String(const char* chars)
int i = 0;
if(chars)
while(chars[i])
i++;
len = i;
str = new char[len - 1];
for(int j = 0; j < len; j++)
str[j] = chars[j];
;
这也是我的完整代码:
#include <iostream>
using namespace std;
class String
public:
char* str;
int len;
String()
str = new char[0];
len = 0;
;
String(const char* chars)
int i = 0;
if(chars)
while(chars[i])
i++;
len = i;
str = new char[len - 1];
for(int j = 0; j < len; j++)
str[j] = chars[j];
;
String(const String& s)
if(s.isEmpty() == false)
len = s.len;
str = new char[len];
for(int i = 0; i < len; i++)
str[i] = s.str[i];
;
~String() noexcept
if(len > 0)
delete[] str;
;
bool isEmpty() const noexcept
if(len == 0)
return true;
else
return false;
unsigned int length() const noexcept
return len;
const char* toChars() const noexcept
char* temp = new char[len];
int c = 0;
while(temp[c] != '\0')
temp[c] = str[c];
c++;
return temp;
;
int main()
const char* chars = "Boo is snoring";
String s;
String tchars;
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
【问题讨论】:
您是否打算在表达式+
中使用str = new char[len - 1];
?您不会复制终止零字符。
您的代码中有几个缺陷——其中一个是您没有在复制构造函数中设置所有成员变量。您不应该关心传入的字符串是否为空,您应该制作一个副本。此外,您还缺少用户定义的赋值运算符。
【参考方案1】:
String(const char* chars)
构造函数的问题在于以下语句:
str = new char[len - 1];
您正在分配 less 内存然后您需要,因此您的 for
循环超出了分配内存的范围。您需要分配至少 len
字符数,而不是len - 1
字符数:
str = new char[len];
如果您打算将str
以null 结尾,则需要分配len + 1
的字符数,然后在循环结束后插入一个null 终止符:
str = new char[len + 1];
for(int j = 0; j < len; j++)
str[j] = chars[j];
str[len] = '\0';
您在 toChars()
方法中犯了类似的错误。您不会以空值终止输出,这是 operator<<
所要求的,您随后将内存传递给:
const char* toChars() const
char* temp = new char[len + 1];
for(int c = 0; c < len; ++c)
temp[c] = str[c];
temp[len] = '\0';
return temp;
请注意,我删除了 toChars()
上的 noexcept
。这是因为new[]
不是noexcept
,如果它无法分配内存,它会抛出std::bad_alloc
异常,而你没有捕捉到。 noexcept
表示该方法根本没有throw
任何异常,但这里不是这种情况。
您的代码也存在其他问题:
如果 len
为 0,即调用 Default 构造函数,或者使用 null/空字符串调用 Converting 构造函数,则析构函数会泄漏内存。如果你调用new[]
,你需要调用delete[]
,不管使用的是len
。
如果要复制的 String
为空,则您的 Copy 构造函数未初始化 str
或 len
。
在main()
中,你不是delete[]
'ing toChars()
返回的内存。
对于这种特殊情况是可选的,但一般来说,您缺少一个复制赋值运算符。而且,由于您显然使用的是 C++11 或更高版本,因此您还应该添加一个 Move 构造函数和一个 Move Assignment 运算符。请参阅Rule of 3/5/0。
试试这个(根据请求不添加额外的库函数):
#include <iostream>
using namespace std;
class String
public:
char* str = nullptr;
unsigned int len = 0;
String() = default;
String(const char* chars)
if (chars)
unsigned int i = 0;
while (chars[i])
++i;
len = i;
str = new char[len];
for(int j = 0; j < len; ++j)
str[j] = chars[j];
String(const String& s)
if (!s.isEmpty())
len = s.len;
str = new char[len];
for(int i = 0; i < len; ++i)
str[i] = s.str[i];
~String() noexcept
delete[] str;
String& operator=(const String &s)
if (&s != this)
String tmp(s);
char *tmpstr = tmp.str;
unsigned int tmplen = tmp.len;
tmp.str = str;
tmp.len = len;
str = tmpstr;
len = tmplen;
return *this;
bool isEmpty() const noexcept
return (len == 0);
unsigned int length() const noexcept
return len;
const char* toChars() const
char* temp = new char[len + 1];
for(unsigned int c = 0; c < len; ++c)
temp[c] = str[c];
temp[len] = '\0';
return temp;
;
int main()
String t"Boo is snoring";
const char *chars = t.toChars();
cout << "t.len : " << t.length() << endl << "toChar() : " << chars << endl;
delete[] chars;
return 0;
虽然,实现toChars()
的更简单方法如下所示:
#include <iostream>
using namespace std;
class String
public:
char* str = nullptr;
unsigned int len = 0;
String() = default;
String(const char* chars)
if (chars)
unsigned int i = 0;
while (chars[i])
++i;
len = i;
str = new char[len + 1];
for(int j = 0; j < len; ++j)
str[j] = chars[j];
str[len] = '\0';
String(const String& s)
if (!s.isEmpty())
len = s.len;
str = new char[len + 1];
for(int i = 0; i < len; ++i)
str[i] = s.str[i];
str[len] = '\0';
~String() noexcept
delete[] str;
String& operator=(const String &s)
if (&s != this)
String tmp(s);
char *tmpstr = tmp.str;
unsigned int tmplen = tmp.len;
tmp.str = str;
tmp.len = len;
str = tmpstr;
len = tmplen;
return *this;
bool isEmpty() const noexcept
return (len == 0);
unsigned int length() const noexcept
return len;
const char* toChars() const noexcept
return str ? str : "";
;
int main()
String t"Boo is snoring";
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
【讨论】:
感谢您的回答!但是我忘了告诉你我不能添加任何其他库。我不允许使用任何标准库功能。你能告诉我其他方式吗?谢谢! @JaeyeonPark 我添加的库函数并没有消除我在原始代码中描述的错误。但无论如何,我已经更新了答案中的代码,只修复了你的错误,没有使用添加的库函数。【参考方案2】:你会崩溃,因为你分配了 len - 1 个字符 new char[len - 1]
,但复制了 len 字符 for(int j = 0; j < len; j++)
并且最后不复制零字符。
第一个构造函数应该是
String(const char* chars)
len = 0;
if (chars)
while (chars[len])
len++;
str = new char[len + 1];
for (int j = 0; j < len; j++)
str[j] = chars[j];
str[len] = 0;
;
希望您能够正确更新第二个构造函数。
析构函数应该是
~String() noexcept
delete[] str;
希望您能够解决其他问题。
【讨论】:
您的构造函数没有考虑chars
是nullptr
的可能性。 std::strlen()
对 nullptr
有未定义的行为,当它是 nullptr
时,您的循环超出了 chars
的范围。此外,应该使用std::copy()
而不是std::memcpy()
,copy()
针对char
等琐碎类型进行了优化,并且可能会在内部使用memcpy()
。
感谢您的回答!但是我忘了告诉你我不能添加任何其他库。我不允许使用任何标准库功能。你能告诉我其他方式吗?谢谢!
好的,有什么问题,你已经演示了strlen
的正确实现。以上是关于字符串类中的字符串(const char *)构造函数内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章
C++ 我将一个const char*的字符串转换为string出错