【问题标题】:Not copying char arrays, function swap doesnt compile correctly and stringPtr is not modified不复制 char 数组,函数 swap 无法正确编译且 stringPtr 未修改
【发布时间】:2026-01-18 10:10:01
【问题描述】:

//在头文件中:类定义:

class myString
{
public:

        myString(void);
        myString(const char *str);

        myString(const myString &); //copy constructor 
        ~myString(void); //destructor

        void swap(myString &from);


private:

        char *stringPtr;
        int stringLen;
};

//在cpp文件中,定义它们的成员函数

myString::myString(const char *str)
{
    stringLen = strlen(str);

    stringPtr = new char[stringLen+1];

    strcpy(stringPtr,str);
    cout << "constructor with parameter called"<<endl;
}

myString::myString(const myString &str)
{

    stringPtr = new char[str.stringLen +1];
    strcpy(stringPtr,str.stringPtr);
    cout << "copyconstructor"<<endl;
}


void myString::swap(myString &from)
{
    myString buffer(from);
    int lengthBuffer = from.stringLen;

    from = new char[stringLen+1];
    from.stringLen = stringLen;
    strcpy(from.stringPtr, stringPtr);


    stringPtr = new char[lengthBuffer+1];
    stringLen = lengthBuffer;
    strcpy(stringPtr,buffer.stringPtr);
}

【问题讨论】:

    标签: c++ arrays pointers char strcpy


    【解决方案1】:

    关于myString::swap() 函数中的错误,您已经得到了一些很好的答案。但是,我想再添加一个。该功能有很多问题,我首先发现很难想到从哪里开始。但后来我意识到你在一些我想指出的基本问题上失败了:

    按照惯例,一个名为swap 的函数预计会执行其任务

    1. 在 O(1) 中
    2. 从未抛出异常。

    (是的,我知道,有例外:std::tr1::array&lt;&gt;::swap()。但这些应该是非常合理的。)您的实施在两个帐户上都失败了。它是 O(n) (strcpy) 并且可能会引发异常 (new)——而且这样做是不必要且没有正当理由的。

    当您查看myString 时,您会发现它只有两条成员数据,它们都是内置类型。这意味着交换这个类的两个对象真的很简单,同时保持上面提到的约定:只需交换成员数据。这就像在他们身上调用std::swap 一样简单:

    void myString::swap(myString &from)
    {
      std::swap(this->stringPtr,from.stringPtr);
      std::swap(this->stringLen,from.stringLen);
    }
    

    这是永远不会失败的(交换两个指针和两个整数不会失败),在 O(1) 中执行,非常容易理解(好吧,无论如何,一旦你掌握了这种交换;这是一种惯用的形式实现特定于类的swap 函数),并且由两行代码组成,调用在标准库中经过良好测试的东西,而不是 8 行代码执行容易出错(在你的情况下是错误的)手动内存管理.

    注意 1:完成此操作后,您应该专门为 std::swap 调用您的类的实现:

    namespace std { // only allowed for specializing function templates in the std lib
      template<>
      inline void std::swap<myString>(myString& lhs, myString& rhs)
      {
        lhs.swap(rhs);
      }
    

    注意 2:为您的班级实施分配的最佳(简单、异常安全和自分配安全)方法是使用其 swap

    myString& myString::operator=(const myString& rhs)
    {
       myString tmp(rhs); // invoke copy ctor
       this->swap(tmp); // steal data from temp and leave it with our own old data
       return *this;
    } // tmp will automatically be destroyed and takes our old data with it
    

    【讨论】:

    • 在赋值运算符中应该检查自赋值。
    • @unknown:不,不应该。交换技巧适用于自分配,而额外的 if 用于优化罕见情况将是对常见情况的悲观。
    【解决方案2】:

    您不能修改参考。即使您用指针替换它,修改指针也不会修改指向的对象。相反,您需要处理参考 - 只需交换字段。

    void myString::swap(myString &from)
    {
        std::swap( stringLen, from.stringLen );
        std::swap( stringPtr, from.stringPtr );
    }
    

    上面使用了 std::swap(),正如 cmets 中 user sbi 所建议的那样。这完全等同于以下内容(仅用于说明,请勿重新发明 STL):

    void myString::swap(myString &from)
        // First remember own length and pointer
        const int myOldLen = stringLen;
        char* myOldPtr = stringPtr;
        // now copy the length and pointer from that other string
        stringLen = from.stringLen;
        stringPtr = from.stringPtr;
        // copy remembered length and pointer to that other string
        from.StringLen = myOldLen;
        from.StringPtr = myOldPtr;
        // done swapping
    }
    

    即使在调用自交换时两者都可以工作:

    myString string;
    string.swap( string );
    

    【讨论】:

    • 所以,你是说void func(int &amp; a) { a = 1; } 不会修改a?
    • 额外的好处是,它可以安全地防止内存分配引起的异常。
    • @vava: from = new char[stringLen+1];会编译,但 myString 没有 operator=() 所以它不会。
    • @sharptooth,它有构造函数,接受char *,所以它会被转换成from = MyString(new char[stringLen + ])并使用默认的operator=来复制它。
    • @sharptooth:你为什么要换自己而不是打电话给std::swap?那真好笑! -1
    【解决方案3】:

    仔细看线

    from = new char[stringLen+1];
    

    一样
    from = MyString(new char[stringLen+1]);
    

    所以你的 MyString 构造函数得到未初始化的字符数组。然后您尝试获取字符串的长度,但strlen 只是循环遍历字符串的字符以查找0 字符。由于我们不知道未初始化的字符数组可能有什么内容,我们不知道strlen 可以返回什么长度。它甚至可以比数组边界更进一步,并使您的程序因段错误而崩溃。但我可以肯定地说,在那之后 from.stringPtr 中没有足够的空间来保存你要复制的字符串。

    所以,请使用from.stringPtr = new char[stringLen+1]; 或更好的from = MyString(*this);,因为您已经有了复制构造函数。

    【讨论】:

      【解决方案4】:

      from = new char[stringLen+1]; 应该是 from.stringPtr = new char[stringLen+1]; 。还记得在分配新内存之前释放之前分配的内存。

      【讨论】: