You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Mystring类移动赋值运算符异常导致后续输出失效问题求助

自定义Mystring类移动赋值运算符异常导致后续输出失效问题求助

我写了一个自定义的Mystring类来练习C++的拷贝和移动语义,代码执行到larry4 = "larry4";都符合预期,但执行larry4 = Mystring{"larry4Again"};之后,后续的larry4.display();、std::cout << "after" << endl;都不工作了,而且我预期的参数化构造函数(arg ctor)+ 移动赋值运算符的输出也没正常走完,完全搞不懂哪里出问题了。

以下是我的代码:

main.cpp

int main()
{
    Mystring larry3 = "larry3"; //will call no-arg ctor not move ctor.
    Mystring larry4 = larry3; // deep copy ctor is being called correctly
    std::cout <<"before" << endl;
    larry4 = "larry4"; // move assignment operator is being called correctly
    larry4 = Mystring{"larry4Again"}; // move assignment should be called -- but things
                                      // seems to be messed after this line
    larry4.display(); // this is also not working
    std::cout <<"after" << endl; // this is also not working

    return 0;
}

Mystring.cpp

//
// Created by kumarg on 17-11-2024.
//

#include "Mystring.h"
#include <iostream>
#include <cstring>

//no-arg ctror
Mystring::Mystring()
  :str{nullptr} {
  str = new char[1];
  *str = '\0'; //
  cout << "no-arg ctor" << endl;
}

//arg ctor
Mystring::Mystring(const char *s)
  :str{nullptr}{
  if(s == nullptr) { //if s points to nullptr then do same thing as no-arg ctor
    this->str = new char[1]; // this->str or str is same used in 3rd line from here
    *str = '\0';
  } else {
    str = new char[std::strlen(s) + 1];
    std::strcpy(this->str, s);
  }
  std::cout << "arg-ctor" <<endl;
}

//dtor
Mystring::~Mystring() {
  std::cout << "dtor called: " << str << endl;
  delete []str;
}

void Mystring::display() const {
  std::cout << this->str << endl;
}

//copy ctor - deep
Mystring::Mystring(const Mystring &source)
  :str{nullptr} {
  std::cout << "deep copy ctor" << endl;
  str = new char[std::strlen(source.str) +1];
  std::strcpy(this->str, source.str);
}

//move ctor
Mystring::Mystring(Mystring &&source)
  :str{source.str} {
  cout << "move ctor" << endl;
  source.str = nullptr;
}

//copy assignment operator implementation
Mystring &Mystring::operator=(const Mystring &rhs) {
  std::cout << "copy assignment operator" << endl;
  //check if both are same already
  if(this == &rhs)
    return *this;
  delete []str;
  str = new char[std::strlen(rhs.str) + 1];
  std::strcpy(this->str, rhs.str);
  return *this;
}

//move assignment operator
Mystring &Mystring::operator=(Mystring &&rhs) {
  std::cout << "move assignment operator" << endl;
  if(this == &rhs)
    return *this;
  delete []str;
  str = rhs.str;
  rhs.str = nullptr;
  return *this;
}

Mystring.h

//
// Created by kumarg on 16-11-2024.
//      

#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>

using namespace std;

class Mystring {
private:
    char *str;
public:
    Mystring(); // default ctor
    Mystring(const char *source); //parameter ctor
    ~Mystring();
    Mystring(const Mystring &source); //copy ctor
    Mystring(Mystring &&source) noexcept; //move ctor

    void display() const;
    Mystring operator-(); // unary - operator overloading
    bool operator==(Mystring &rhs); // assignment operator overloading
    bool operator!=(Mystring &rhs); // !- operator overloading


    bool operator<(Mystring &lhs);
    bool operator>(Mystring &lhs);
    Mystring operator+(Mystring &lhs);
    //assignment (=) operator overloading, remember this called when we do s2 = s1(both are initilaized already)
    // = operator can be overloaded in 2 ways, copy and move, if l-value thenp copy will be called, if r-value move will be called
    Mystring &operator=(const Mystring &rhs);
    // = op overloading by move
    Mystring &operator=(Mystring &&rhs);
    //Mystring &operator+=(Mystring &lhs);    
};

#endif //MYSTRING_H

问题分析与解决办法

你遇到的核心问题是析构函数中的未定义行为导致程序崩溃,进而使得后续代码无法执行,咱们一步步理清楚:

当执行larry4 = Mystring{"larry4Again"};时,实际会发生这些步骤:

  1. 创建临时Mystring对象Mystring{"larry4Again"},调用参数化构造函数(arg-ctor),此时临时对象的str指向堆上的"larry4Again"字符串内存。
  2. 触发移动赋值运算符,把临时对象的str所有权转移给larry4,同时将临时对象的str设置为nullptr(这是移动语义的标准操作,避免双份释放)。
  3. 临时对象的生命周期结束,触发析构函数。

而你的析构函数里有这行代码:

std::cout << "dtor called: " << str << endl;

C++标准里明确规定,用std::cout输出一个nullptr类型的char*属于未定义行为——不同编译器的处理逻辑不一样,有的会输出(null),有的直接会导致程序崩溃。一旦程序崩溃,后续的larry4.display();和cout << "after"自然就没机会执行了。

另外还有两个小细节需要修正,养成良好的编码习惯:

  1. 移动构造函数的实现要和头文件声明一致,加上noexcept:

    Mystring::Mystring(Mystring &&source) noexcept
      :str{source.str} {
      cout << "move ctor" << endl;
      source.str = nullptr;
    }
    

    标准库容器在使用移动语义时会依赖noexcept来保证异常安全性,虽然这里没用到容器,但保持声明和实现一致很重要。

  2. 像operator==、operator!=这类比较运算符,应该把参数设为const引用,同时成员函数本身也设为const,这样才能支持对const对象调用这些方法:

    // 头文件里的声明要改成这样
    bool operator==(const Mystring &rhs) const;
    bool operator!=(const Mystring &rhs) const;
    bool operator<(const Mystring &rhs) const;
    bool operator>(const Mystring &rhs) const;
    Mystring operator+(const Mystring &rhs) const;
    

修复步骤

最关键的是修改析构函数,先判断str是否为nullptr再输出:

Mystring::~Mystring() {
  std::cout << "dtor called: ";
  if (str != nullptr) {
    std::cout << str;
  } else {
    std::cout << "nullptr";
  }
  std::cout << endl;
  delete []str;
}

修改后再运行代码,你就能看到预期的输出顺序了:

arg-ctor
move assignment operator
dtor called: nullptr
larry4Again
after

后续的display()和cout也能正常执行啦。

备注:内容来源于stack exchange,提问作者gaurav s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 02:58:06