【问题标题】:string class assignment operator overloading in c++C++中的字符串类赋值运算符重载
【发布时间】:2013-08-10 00:04:35
【问题描述】:

我定义了自己的字符串类 MyString。一切正常,直到我通过重载的 operator= 将一个对象分配给另一个对象。我知道问题出在哪里,但我不知道如何解决。有什么帮助吗?

class MyString{
public:
    MyString( const MyString *strIni );
    MyString( const char *str);
    ~MyString();
    MyString& operator=( const MyString &str );
private:
    char *str;
}


MyString::MyString( const MyString *strIni ){
    this->str = new char[strlen(strIni->str)+1];
    strcpy(this->str,strIni->str) ;
};


MyString::MyString( const char *str){
    this->str = new char[ strlen(str) + 1 ];
    strcpy(this->str , str);
};

MyString::~MyString(){
    delete [] this->str ;
    cout << "successfully deleted..." << endl;
};


MyString& MyString::operator=( const MyString &str ){
    // temp obj holding the rhs
    MyString strTmp(str);
    // temp char pointer holding the rhs
    char *cTmp = strTmp.str;
    // temp obj holding this, later release this memory 
    strTmp.str = this->str ;
    // this holding rhs; assignment done.
    this->str = cTmp;
    return *this ;
};


int main(){
    {                                  // line 1
        MyString mystr1("string #1");  // line 2
        MyString mystr2("string #2");  // line 3
        mystr1 = mystr2;               // line 4
    }                                  // line 5
    return 0;
}

代码的问题是:在第4行,赋值后两个对象mystr1和mystr2中的指针都指向同一个字符串“string#2”。当程序在第 5 行跳出括号时,会自动按顺序调用析构函数:mystr2 和 mystr1。 mystr2 被破坏后,“string#2”的内存已经被释放。当 mystr1 的析构函数试图释放不存在的内存时,程序崩溃了。

任何人都可以帮助我修复重载的成员函数。当我分配 mystr1 = mystr2 时,我可以创建一个新字符串,而不是让两个指针指向同一个字符串。

非常感谢!!



更多问题的更新...... 谢谢吨!

实际上,我在重载函数中使用了复制和交换。根据@Mateusz Kołodziejski 的建议,我对其进行了修改:

MyString& MyString::operator=( const MyString &rhs ){
    if( this != &rhs ){
        // copy using constructor
        MyString strTmp(rhs) ;
        // swap        
        char *cTmp = strTmp.str;
        // strTmp will be destructed, thus the memory in this will be released
        strTmp.str = this->str ;
        // size of rhs
        const int str_size = strlen(rhs.str);
        this->str = new char[str_size+1];
        copy(rhs.str,rhs.str+str_size,this->str);
    }
    return *this ;
};

当调用析构函数时,不会崩溃。但是如果添加了打印输出成员函数,似乎又出现了一个问题:

void MyString::printout(){
    int str_size = strlen(this->str);
    cout << "string size: " << str_size << endl ;
    for( int i=0;i<str_size;i++ ){
        cout << *(this->str + i);
    }
}

在主函数中:

int main(){
    {                                  
        MyString mystr1("string #1");  
        MyString mystr2("string #2");  
        mystr1.printout();
        mystr2.printout();
        mystr1 = mystr2;  
        cout << "after assignment: " << endl;
        mystr1.printout();
        mystr2.printout();             
    }                                  
return 0;
}

结果是:

string #1
string #2
after assignment...
string #2═²²²²
string #2

好像mystr1不正常……

谁能帮我解释一下?

谢谢大家!!

【问题讨论】:

  • 我当然希望这是用于学校作业或类似的事情,因为std::string 已经足够好了。
  • 请改用MyString&amp; MyString::operator=( const MyString &amp;rhs ){。太多的strs 表示不同的东西。或许这样问题就会更清楚了。
  • 不要重新设计轮子?这种错误就是为什么你应该使用现有的、有据可查的和经过测试的类。在这种情况下std::string。不过,您可能需要调查 strdup 以在赋值运算符中复制字符串。
  • @nyarlathotep 是的,我刚刚意识到,这就是问题所在,我为此添加了答案。

标签: c++ string overloading destructor operator-keyword


【解决方案1】:

这里的问题在于声明

MyString strTmp(str);

在赋值运算符中,您调用默认隐式生成的复制构造函数。并且该构造函数只会复制指针,而不是创建新指针并复制字符串的内容。

您采用MyString 指针的构造函数不是复制构造函数,复制构造函数将采用常量引用。如果您将构造函数更改为使用指向MyString 的指针来获取引用,那么它会更好地工作。

【讨论】:

  • 即使 OP 确实创建了一个深拷贝的复制构造函数,赋值运算符仍然会使用 temp 对象中的 char*,然后将其删除,对吧?
  • @nyarlathotep 不,OP 交换了指针,所以删除的是旧的 this-&gt;str
  • 啊,是的,错过了 - 可能是 std::swap 以获得更好的可读性;)
【解决方案2】:

您显然必须修复您的 operator=() 实现。

#include <algorithm>

MyString& MyString::operator=( const MyString &rhs ) // (1)
{
    if (this != &rhs) // (2)
    {
        delete[] this->str; // (3)
        this->str = NULL;

        const int str_length = strlen(rhs.str);

        this->str = new char[str_length + 1];
        this->str[str_length] = '\0';
        std::copy(rhs.str, rhs.str + str_length, this->str); // (4)
    }

    return *this;
}

1) 使用“rhs”(右侧)而不是“str”作为变量名称以避免歧义。

2) 始终检查您的对象是否未分配给自身。

3) 在分配新内存之前释放旧分配的内存。

4) 将 rhs 的内容复制到 this->str,而不是仅仅重定向指针。

编辑:

添加了这个->str = NULL;避免对可能的新异常和以后对象解构的双重删除。

这是一个幼稚的实现——你必须知道 new 可以抛出异常。正如@nyarlathotep 所建议并在此处描述的那样,此处的复制交换习语会更好:Copy-and-swap

【讨论】:

  • 请注意:您的代码不提供“强”异常安全性:如果 new char 抛出异常,this-&gt;str 仍然指向未分配的空间(之前删除的内存)。这就是为什么交换习语在这里更受欢迎的原因。
  • 重新考虑它甚至不提供基本的异常安全性,因为每当 MyString 被销毁时,str 就会被第二次删除......
  • @nyarlathotep 没错,这只是一个幼稚的实现。 Copy-swap 成语确实会更好。
  • @nyarlathotep 已编辑。
  • 感谢您的 cmets。这是一道面试题,不是作业。我实际上在运算符重载函数中使用了复制和交换。
猜你喜欢
  • 2013-02-08
  • 2016-04-01
  • 2017-10-07
  • 2013-03-30
  • 2016-08-30
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
  • 2015-06-01
相关资源
最近更新 更多