问题
So I was trying to implement String class and I got an error saying segmentation fault. I think there is memory leak in my constructor. Can you please tell me what I am doing wrong? Thank you.
here is the constructor code and I am not permitted to use any standard library functionality.
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];
}
};
And also this is my full code:
#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 t{chars};
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
}
回答1:
The problem with your String(const char* chars)
constructor is on this statement:
str = new char[len - 1];
You are allocating less memory then you need, so your for
loop is going out of bounds of the allocated memory. You need to allocate at least len
number of chars, not len - 1
number of chars:
str = new char[len];
If you intend str
to be null-terminated, you need to allocate len + 1
number of chars instead, and then insert a null terminator after the loop is finished:
str = new char[len + 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
str[len] = '\0';
You are making a similar mistake in your toChars()
method. You are not null-terminating the output, which is required by the operator<<
that you are passing the memory to afterwards:
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;
}
Note that I removed the noexcept
on toChars()
. This is because new[]
is not noexcept
, it can throw a std::bad_alloc
exception if it can't allocate memory, which you are not catching. noexcept
means the method doesn't throw
any exception at all, but that is not the case here.
There are other problems with your code, too:
Your destructor leaks memory if
len
is 0, ie if your Default constructor is called, or your Converting constructor is called with a null/empty string. If you callnew[]
, you need to calldelete[]
, regardless of thelen
used.Your Copy constructor is not initializing
str
orlen
if theString
being copied is empty.In
main()
, you are notdelete[]
'ing the memory thattoChars()
returns.optional for this particular situation, but in general, you are missing a Copy Assignment operator. And, since you are clearly using C++11 or later, you should also add a Move constructor and a Move Assignment operator as well. See the Rule of 3/5/0.
Try this instead (with no additional library functions added, per request):
#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;
}
Although, a simpler way to implement toChars()
would look like this instead:
#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;
}
回答2:
You get crash cause you allocate len - 1 chars new char[len - 1]
, but copy len chars for(int j = 0; j < len; j++)
and do not copy zero-char in the end.
The first constructor should be
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;
};
I hope you are able to update the second constructor properly.
The destructor should be
~String() noexcept{
delete[] str;
}
I hope you are able to fix other issues.
来源:https://stackoverflow.com/questions/61067994/stringconst-char-constructor-memory-leak-in-string-class