我试图在析构函数中使用delete[]
来删除已创建的指针。使用delete[] string;
时,我得到了垃圾。如果没有delete
,我的代码就会出现:
class test
{
public:
test operator=(char* tests)
{
this->string = tests;
return *this;
}
int starlen()
{
i = 0;
while (this->string[i] != '\0')
i++;
return i;
}
test operator+(test& tests)
{
a = strlen(this->string);
b = strlen(tests.string);
ptr = this->string;
ptr2 = tests.string;
for (int i = a; i < a + b; i++)
{
*(ptr + i) = *(ptr2 + i - a);
}
ptr[a + b] = '\0';
return ptr;
}
void printit()
{
cout << string;
}
test()
{
string = 0;
}
test(char* ptr)
{
string = new char[10];
strcpy(string, ptr);
}
~test()
{
//delete[] string; ????
}
public:
char* string;
int a;
int b;
char* ptr;
char* ptr2;
int i;
};
int main()
{
test t1("book");
test t2("shelf");
test t3;
t3 = t1 + t2;
cout << t3.starlen() << endl;
t3.printit();
return 0;
}
答案 0 :(得分:3)
您的析构函数失败,因为您没有正确关注Rule of Three:
三规则(也称为三巨头或三巨头的规则)是C ++(在C ++ 11之前)的经验法则,声称如果一个类定义了一个(或更多)以下它应该明确定义所有三个:
- 析
- 复制构造函数
- 复制分配运算符
您的类缺少复制构造函数,它接受test
个对象作为输入。您有一个转换构造函数,它接受char*
作为输入,但如果该输入超过9个字符,则在复制字符时会丢弃周围的内存。
您的班级缺少一个接受test
对象作为输入的复制赋值运算符。您有转换分配运算符,它接受char*
作为输入,但它正在使用char*
的所有权而不是复制字符数据,它正在泄漏先前分配的string
缓冲区。更糟糕的是,它返回正在修改的test
对象的副本,而不是将引用返回给对象,并且该副本不起作用正确的,因为你缺少复制构造函数。因此,最终会有多个test
对象指向内存中相同的string
缓冲区,因此您将在同一内存中多次调用delete[]
析构函数。
此外,您的operator+
也未正确实施。它返回一个新的test
对象,但它没有分配占两个源字符串总长度的新内存。您只需将右侧test
对象中的字符数据直接复制到左侧char*
对象的test
字符串中,而不先将其展开。所以你在摧毁周围的记忆。这个operator+
应该返回一个新的test
对象,它是连接在一起的输入字符串的副本,而根本不修改任何一个源字符串。
尝试更像这样的东西:
class test
{
public:
// default constructor
test()
: string(0)
{
}
// copy constructor
test(const test &src)
: string(new char[src.starlen()+1])
{
strcpy(string, src.string);
}
// converting constructor
test(const char* src)
: string(new char[strlen(src)+1])
{
strcpy(string, src);
}
// destructor
~test()
{
delete[] string;
}
// copy assignment operator
test& operator=(const test &rhs)
{
if (&rhs != this)
std::swap(string, test(rhs).string);
return *this;
}
// converting assignment operator
test& operator=(const char *rhs)
{
std::swap(string, test(rhs).string);
return *this;
}
int starlen() const
{
return strlen(string);
}
test operator+(const test& rhs) const
{
int a = starlen();
int b = rhs.starlen();
test ret;
ret.string = new char[a+b+1];
strcpy(ret.string, string);
strcpy(ret.string+a, rhs.string);
return ret;
}
void printit() const
{
std::cout << string;
}
private:
char* string;
};
int main()
{
test t1("book");
test t2("shelf");
test t3;
t3 = t1 + t2;
cout << t3.starlen() << endl;
t3.printit();
return 0;
}
如果您使用的是C ++ 11或更高版本,您也应该关注Rule of Five:
随着C ++ 11的出现,C ++ 11实现了移动语义,允许目标对象从临时对象中抓取(或窃取)数据,因此可以将规则扩展为五。以下示例还显示了新的移动成员:移动构造函数和移动赋值运算符。因此,对于五的规则,我们有以下特殊成员:
- 析
- 复制构造函数
- 移动构造函数
- 复制分配操作员
- 移动分配运算符
class test
{
public:
// default constructor
test()
: string(0)
{
}
// copy constructor
test(const test &src)
: string(new char[src.starlen()+1])
{
strcpy(string, src.string);
}
// converting constructor
test(const char* src)
: string(new char[strlen(src)+1])
{
strcpy(string, src);
}
// move constructor
test(test &&src)
: string(0)
{
std::swap(string, src.string);
}
// destructor
~test()
{
delete[] string;
}
// copy assignment operator
test& operator=(const test &rhs)
{
if (&rhs != this)
std::swap(string, test(rhs).string);
return *this;
}
// converting assignment operator
test& operator=(const char *rhs)
{
std::swap(string, test(rhs).string);
return *this;
}
// move assignment operator
test& operator=(test &&rhs)
{
std::swap(string, rhs.string);
return *this;
}
int starlen() const
{
return strlen(string);
}
test operator+(const test& rhs) const
{
int a = starlen();
int b = rhs.starlen();
test ret;
ret.string = new char[a+b+1];
strcpy(ret.string, string);
strcpy(ret.string+a, rhs.string);
return ret;
}
void printit() const
{
std::cout << string;
}
private:
char* string;
};
话虽这么说,这可能只是一个学习如何编写自定义字符串类的练习。但是,C ++标准定义了std::string
类,您应该使用它。如果你想要自己的类,你可以委托给std::string
,让编译器和STL为你做繁重的工作:
class test
{
public:
// default constructor
test()
{
}
// copy constructor
test(const test &src)
: str(src.str)
{
}
// converting constructor
test(const std::string &src)
: str(src)
{
}
// move constructor
test(test &&src)
: str(std::move(src.str))
{
}
int starlen() const
{
return str.length();
}
test operator+(const test& rhs) const
{
return str + rhs.str;
}
void printit() const
{
std::cout << str;
}
private:
std::string str;
};
答案 1 :(得分:0)
删除内部字符串的责任是谁?我注意到你将字符串的内存分配给没有构造函数的t3,然后在两次相同的内存地址上调用析构函数。此外,你的算子+,应对很可能会写入传递给它的数组的末尾。你至少应该有一个strlen()结束检查,以防止发生。如果你的缓冲区已经损坏,那么任何对delete []的调用都会因堆上的内存覆盖而抱怨。
请参阅Remy的答案,找到一个更好的解决方案,正确处理字符串操作和内存分配问题(非常复杂)的性质。从我能看到的情况来看,答案甚至是例外。