我试图实现字符串类。这就是我所做的:
#include <iostream>
#include <cstring>
using namespace std;
class MyString{
private:
char * content;
int length;
public:
MyString ();
MyString ( const char * );
~MyString ();
MyString ( const MyString & );
void print ( void );
void operator = ( const MyString );
};
MyString :: MyString () {
content = 0;
length = 0;
}
MyString :: MyString(const char *n) {
length = strlen (n);
content = new char [ length ];
for ( int i = 0 ; i < length ; i++ ){
content [i] = n [i];
}
content [length] = '\0';
}
MyString :: ~ MyString () {
delete [] content;
content = 0;
}
MyString :: MyString ( const MyString & x ) {
length = x.length;
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
content [length] = '\0';
}
void MyString :: print( void ) {
cout <<""<< content << endl;
}
void MyString :: operator = ( const MyString x ) {
length = x.length;
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
content [length] = '\0';
}
int main() {
MyString word1 ("stackoverflow");
MyString word2;
word2 = word1;
word1.print();
word2.print();
}
我编译了它,这就是我得到的:
计算器
计算器
进程返回0(0x0)执行时间:0.050秒 按任意键继续。
虽然根据上面的结果看起来是正确的,但我想知道它是否真的正确?我不太熟悉C风格的字符串,所以我很担心 例如关于line:
content [length] = '\0';
由于C风格的字符串末尾有空终结符,我想终止我的数组,但这是正确的方法吗? 我使用了动态内存分配,我也想知道我是否正确地释放了资源? 是否有一些内存泄漏? 提前谢谢。
EDIT1: 我也重载了opeartor +(我想加入&#34; MyStrings&#34;),这里是代码:
MyString MyString :: operator + ( const MyString & x ){
MyString temp;
temp.length = x.length + length;
temp.content = new char [ temp.length + 1 ];
int i = 0, j = 0;
while ( i < temp.length ) {
if (i < length ) {
temp.content [i] = content [i];
}
else {
temp.content [i] = x.content [j];
j ++;
}
i ++;
}
temp.content [ temp.length ] = '\0';
return temp;
}
这是主程序:
int main()
{
MyString word1 ( "stack" );
MyString word2 ( "overflow" );
MyString word3 = word1 + word2;
word3.print();
word3 = word2 + word1;
word3.print();
}
结果如下:
计算器
overflowstack
进程返回0(0x0)执行时间:0.040秒 按任意键继续。
我希望这段代码没有问题:)
EDIT2: 这是使用for循环的+运算符的实现,而不是while:
MyString MyString :: operator + (const MyString & x){
MyString temp;
temp.length = x.length + length;
temp.content = new char [temp.length+1];
for( int i = 0 ; i < length ; i++ ){
temp.content[i] = content[i];
}
for( int i = length , j = 0 ; i <temp.length ; i++, j++){
temp.content[i] = x.content[j];
}
content[temp.length] = '\0';
return temp;
}
现在可能会更好,因为没有if:)
答案 0 :(得分:3)
您正在尝试为content[length]
分配一个值,但是您没有为content[length]
分配足够的内存来访问。如果length == 10
,则您可以访问content[0]
至content[9]
,但不能访问content[10]
。
这可以通过从两个构造函数中删除行content[length] = \0
来修复,或者如果要追加\0
,则应该length
增加1
的值
您是否考虑过在内部使用std::string
?
编辑:@Thane Plummer首先在评论中指出这一点!
答案 1 :(得分:2)
有两个错误。 Thane Plummer在评论中已经说过一个,Tas在答案中已经说过:
MyString :: MyString(const char *n) {
length = strlen(n);
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
content [length] = '\0';
}
如果你的字符串是空终止&#34; abc \ 0&#34;,strlen将返回3而不是4,所以你只会分配3个字符而不是4个字符(编辑:并且要完成,如前所述,你确实开始从0而不是1开始索引,因此即使你增加长度,内容[length]也会一直溢出)
另一个错误不那么严重(实际上是合法但奇怪的c ++):
void operator = ( const MyString );
复制赋值运算符应该使用const引用而不是const值(否则您可能无用地调用复制构造函数),并返回引用而不是void(这样您就可以链接一些调用)。正确的声明是:
MyString& operator=(const MyString&);
正确的实施是:
MyString& MyString::operator=(const MyString& x) {
length = x.length;
delete[] content;
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
// actually not needed since x.content should already be null-terminated
// content[length - 1] = '\0';
return *this;
}
答案 2 :(得分:2)
其他一些注意事项和建议,因为至少还有两个陷阱等待跳出来罢工。
#include <iostream>
#include <cstring>
// using namespace std; DANGER! namespace std is huge. Including all of it can
// have tragic, unforeseen consequences. Just use what you need.
using std::cout;
using std::endl;
class MyString
{
private:
char * content;
int length;
// will use clone to reduce duplication in the copy constructor and operator =
void copy(const MyString & source);
public:
MyString();
// it is nice to name the variables in the definition. The header may be the
// only documentation the user gets.
MyString(const char * source);
~MyString();
MyString(const MyString &source);
void print(void);
// changed prototype to match the expected format operator= format
MyString & operator =(const MyString &source);
//OP asked about this in a previous question.
friend std::ostream & operator<<(std::ostream & out,
const MyString& towrite);
};
MyString::MyString()
{
// content = 0;
//destructor needs something to delete[]. If content isn't set to something,
//you'll get a big ka-BOOM! when the MyString is destroyed
content = new char[1];
content[0] = '\0'; //this has the advantage of printing an empty MyString
// without crashing
length = 0;
}
MyString::MyString(const char *source) // Variable names should describe their purpose
{
//DANGER: strlen will fail horribly if passed an unterminated string. At a
// loss at the moment for a good, safe solution. Look into strnlen, but
// even it can't help you here.
length = strlen(source);
content = new char[length + 1]; //Needed one extra character to fit the NULL
/* If we got this far without dying, strcpy is no threat which makes this redundant:
for (int i = 0; i < length; i++)
{
content[i] = n[i];
}
content[length] = '\0';
*/
strcpy(content, source);
}
MyString::~MyString()
{
delete[] content;
// content = 0; string is gone. No need to clear this
}
void MyString::copy(const MyString & source)
{
length = source.length;
content = new char[length + 1];
// assuming that the source MyString is correctly formatted this is once again safe.
strcpy(content, source.content);
}
MyString::MyString(const MyString & source)
{
copy(source); // use the copy method
}
void MyString::print(void)
{
cout << "" << content << endl;
}
MyString &MyString::operator =(const MyString &source)
{
copy(source); // use the copy method again.
return *this; // allows chaining operations
}
std::ostream & operator<<(std::ostream & out,
const MyString& towrite)
{
out << towrite.content;
return out;
}
int main()
{
MyString word0;
MyString word1("stackoverflow");
MyString word2;
word2 = word1;
MyString word3(word2); //testing copy constructor
word1.print();
word2.print();
cout << word3 << endl; //testing outstream overload
// test output of empty string
word0.print();
cout << word0 << endl;
}
编辑:
在发布之后意识到,因为我们知道字符串的长度,所以使用memcpy(content, source.content, length+1);
代替strcpy
会有显着的性能提升。