繁体   English   中英

使用复制构造函数和复制赋值运算符获取指向动态分配内存的指针

[英]Using copy constructor and copy assignment operator for pointers to dynamically allocated memory

我正在查看有关“三法则”的解释,并发现以下代码:

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

解释事情(在这里: 什么是三人制? )我不确定是否应该在那儿问这个问题(因为它已经发布了一段时间了)还是提出一个新问题,所以我希望我不要不会惹麻烦。 我只想看看我是否正确理解了此代码?

根据我的理解,要复制的对象不是复制指针(因此使两个对象指向同一内存),而是动态分配新的内存,该内存将包含与复制对象相同的数据。包含? 拷贝分配运算符函数的开头有“ delete [] name]”的原因是什么? 是因为使用了=时,指针会被自动复制,因此该新对象指向与复制对象相同的内存,而delete [] name在动态为其分配新的内存以指向它之前删除该内存? 其实,我不确定我要说的是什么,所以有人可以向我解释为什么使用删除吗? 最后,if(this!=&that)部分的目的是什么?

谢谢!

delete是释放this对象先前为name字段拥有的内存。 注意,这是赋值运算符的重载,即:

A x,y;
x = y;

将使用this == &xthat == &y调用函数。

x.name y的内部成员复制到x,同时确保释放先前分配给x.name任何动态分配的内存

成员变量name是一个指针。 它(几乎总是)指向在堆上分配的char[] (即,使用new )。

我们要将另一个对象的名称复制到该对象:

strcpy(name, that.name);

但是在此之前,我们必须确保name指向的数组足够大以容纳新名称(以及终止的'\\ 0'),因此我们要为空间分配new

name = new char[strlen(that.name) + 1];

但是 ,该name先前指向的空间呢? 我们不想只是放弃它,那将是内存泄漏 ,因此我们应该在重新分配指针之前将其delete

delete[] name;

至于this != &that ,请考虑如果某个粗心的人使用一个person到自己的赋值运算符( Alice = Alice )会发生什么。 完成这些步骤,您会发现名称将完全丢失。

首先,这个赋值运算符是一个不好的例子 特别是它不是线程安全的! 如果要编写适当的赋值运算符,可以这样编写:

person& operator=(person other) {
    other.swap(*this);
    return this;
}

其中swap()是您仍然想要的成员函数,只需交换所有成员:

void swap(person& other) {
    std::swap(this->name, other.name);
    std::swap(this->age,  other.age);
}

关于您的问题:

  1. 作者是否有帮助检查if (this != &that)表示代码错误! 基本上有两种原因是错误的:
    1. 这种比较的目的是防止自我分配。 如果此检查后的代码实际上依赖于此检查是否正确,则几乎可以肯定不是异常安全的(罕见地,此后的代码提供了基本保证;我从未见过最终实现强异常安全性的示例)尽管没有理由让分配操作者不执行强异常安全保证)。
    2. 如果代码只是存在而并非严格需要,那么它就是“优化”自赋值情况。 ...并影响到所有不是自派任务的情况,希望这是绝大多数! 如果您的代码主要是在忙于为其分配对象,那么代码中还有一些更根本的问题。
  2. C ++没有任何垃圾回收:如果分配了内存,则需要释放内存(当然,其他所有资源也是如此)。 使用new在C ++中分配内存有两种形式:
    1. 您可以使用new T(args)new T{args}分配单个对象,其中T不是数组类型的typedef ,而args是构造函数参数的占位符。 这样分配的内存需要使用delete ptr释放,其中ptr是从上面的表达式返回的结果。 通常,不会显式释放内存,而是将其传递给负责释放内存的对象,例如std::unique_ptr<T>(new T(args))std::unique_ptr<T>的析构函数将调用根据需要delete
    2. 您可以分配一个数组对象,通常使用new T[n] ,其中n是数组的大小。 这样分配的对象需要使用delete[] array释放,其中array是从数组分配返回的结果。 通常很少看到这种情况,您宁愿使用std::stringstd::vector<T>分配数组。 您也可以使用上面提到的类模板,但需要指出需要释放一个数组: std::unique_ptr<T[]>(new T[n])

delete[] name;的原因delete[] name; 类不变式是name指向动态分配的缓冲区,该缓冲区的大小恰好与该人的姓名相同。 注意,我们将创建一个新的缓冲区,以容纳存储在that.name中的数据副本。 如果我们不delete[]原始name所指向的内容,则会泄漏该内存。

至于if (this != &that) ,只要在脑海中追踪如果不存在并且有人在x = x person x调用x = x会发生什么。 首先,我们delete[] name 由于this == &that ,这也意味着this->name == that.name ,因此我们也使that.name指向的缓冲区无效。 在下一步中,我们在(现在已失效)的缓冲区上调用strlen() ,它给出了未定义的行为(例如,可能是段错误)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM