【问题标题】:C++ Object CopyC++ 对象复制
【发布时间】:2020-08-29 16:02:55
【问题描述】:

我只是在看下面的代码sn-p:

#include<iostream>
#include<cstring>
using namespace std;

class String
{
private:
    char *s;
    int size;
public:
    String(const char *str = NULL); // constructor
    ~String() { delete [] s; }// destructor
    void print() { cout << s << endl; }
    void change(const char *); // Function to change
};

String::String(const char *str)
{
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}

void String::change(const char *str)
{
    delete [] s;
    size = strlen(str);
    s = s + 1;
    s = new char[size+1];
    strcpy(s, str);
}

int main()
{
    String str1("StackOverFlow");
    String str2 = str1;

    str1.print();
    str2.print();

    str2.change("StackOverFlowChanged");

    str1.print();
    str2.print();
    return 0;
}

我希望输出为: 堆栈溢出, 堆栈溢出, 堆栈溢出, StackOverflowChanged。

str2.change("StackOverFlowChanged") 行之前,str1 和 str2 的 s 都指向同一个内存位置。但是,在change方法中,由于指针值发生了变化,我除了现在str1和str2的s指向不同的位置,事实并非如此。有人能解释一下为什么会这样吗?

【问题讨论】:

    标签: c++


    【解决方案1】:

    调用str2.change 后,str1 不再有有效指针。由于两个对象共享同一个指针,通过删除一个对象中的数组,另一个对象现在指向一个已删除的数组。试图访问str1 指向的数组是未定义的行为,所以str1.print() 是无效代码。

    现在,在这种特殊情况下str2.change 中的 new char[] 很有可能恰好返回一个指向与该地址相同的地址的指针那只是被删除了。毕竟,内存刚刚被释放,同时没有进行其他分配。因此,虽然str1 的指针仍然无效,但恰好str1.print 被调用时指向了一个有效的字符串。

    但这只是偶然;实现不必这样做。未定义的行为是未定义的,你需要properly follow the Rule of 5。或者直接使用std::string

    【讨论】:

    • 如果我错了,请纠正我,我知道我实际上是通过在堆上创建内存位置来覆盖指针地址。因此,声明 s = s + 1 没有任何意义。
    • @eet:我不知道您所说的“通过在堆上创建内存位置来覆盖指针地址”是什么意思。但是你说s = s + 1什么也没做是对的;即使它是明确定义的行为(它不是,因为此时s 是一个无效指针),你将要覆盖s。更改将要被某些东西覆盖的变量的值是没有意义的。
    【解决方案2】:

    问题是你在str1指向的内存上调用了delete[]

    然后允许运行时库为下一次分配调用重用相同的内存,显然这就是发生的事情。

    str1 没有改变它指向的内存,但是内容改变了。

    请注意,在 delete[]d 之后使用指向内存的指针是未定义的行为。所以确实任何事情都可能发生。

    当您编写拥有堆分配内存的类时,应非常注意谁拥有该内存以及何时释放该内存;特别要注意复制构造函数和赋值运算符,因为在使用原始指针拥有内存的情况下,自动生成的代码很少是正确的。

    【讨论】:

      【解决方案3】:

      不,str1 和 str2 在 str2.change("StackOverFlowChanged") 行之前不指向同一个内存位置。 str2 位于不同的位置,但与 str1 具有相同的值。您可以使用 -

      来验证这一点
      cout << &str1 << '\t' << &str2 << endl;
      

      它会显示它们在内存中的位置不同。

      【讨论】:

      • str1 和 str2 不指向同一个内存,但 srt1.s 和 str2.s 会!尝试将所有成员设为公开并输入std::cout&lt;&lt;static_cast&lt;const void*&gt;(str1.s)&lt;&lt;"\t"&lt;&lt;static_cast&lt;const void*&gt;(str2.s)&lt;&lt;"\n";
      猜你喜欢
      • 2019-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-04
      • 1970-01-01
      相关资源
      最近更新 更多