复制赋值运算符说明

copy assignment operator clarification

我正在为我创建的 class 编写一个复制赋值运算符,我使用以前的 post 作为指南: What is The Rule of Three?

我对这个人解释的一方面有点困惑。

这是他们的 class:

class person
{
    char* name;
    int age;
};

这是我用作参考的复制赋值运算符定义(至少提供了 2 个):

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

令我感到困惑的是,为什么他们要包括 delete[] name; 行?

这是他们提供的另一个例子:

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;
}

我立即回避了这个,因为我不明白为什么函数检查 if(this != &that) 然后在一个似乎没有生成的数组上运行 delete (delete[] name;)然而。调用赋值运算符时,是否会在调用复制赋值运算符函数之前立即调用常规构造函数?因此意味着我们必须删除由 classes 构造函数生成的数组,因为它只能充满垃圾数据?

为什么我不能只写: name = that.name

name = new char[that.name.size()];
for (int i = 0; i < that.name.size(); i++)`
{
  name[i] = that.name[i]
}

这可能是非常基本的,我应该只实现 post 建议的内容,但我的实际用例涉及一个包含多个成员的 struct 数组,所以我只有一点点很难理解我到底需要做什么。

我知道这里有 2.5 个问题。任何见解将不胜感激。

这是我第一次实现自定义复制构造函数和复制赋值运算符,我相信在我完成几次之后它看起来会很容易。

提前致谢。

What I'm finding confusing is, why are they including the line delete[] name;?

当使用 newnew[] 管理内存时,根据经验,它们应该有匹配的 deletedelete[]。据推测,name 之前是用 new[] 分配的(在构造函数中或之前的赋值),所以在下一行用 name = local_name; 重新分配它之前,我们需要 delete[]它。

I couldn't understand why the function checks if(this != &that)

此检查是完整性检查。如果您将对象分配给自身,则无需执行任何操作。事实上,它可能会导致问题,因为如果你 delete[] name;this 指向与 that 引用相同的对象时,那么你不能再复制 that.name 因为它的内存已经释放了。

Why can't I just write: name = that.name or

这将使两个不同实例中的 name 指向同一内存。在某些情况下,这样的共享内存不仅有用而且需要。但是,您必须小心,因为如果其中一个实例对其名称执行 delete[],则另一个实例中的 name 将不再有效。

所有这些都是确保正确管理内存所必需的。

如果我理解正确,您需要回答 operator= 实际调用的时间?
好吧,当存在两个有效对象时 operator= 总是被调用 。其中之一被分配给,第二个是数据源。此操作后,两个对象必须仍然有效

这意味着在 operator= 内部你有 this 对象,其内存分配给一个字符串(在其中一个构造函数中分配)和 that 对象,也分配了内存对于另一个字符串。

What I'm finding confusing is, why are they including the line delete[] name;?

我们必须首先清理当前驻留在 this 对象中的内存,否则我们会在为它分配新的 momry 后丢失这个指针。

When the assignment operator is invoked, is the regular constructor called immediately before the copy assignment operator function is called?

没有。该对象已经存在,这就是调用 operator= 的原因(而不是复制构造函数)。

Therefore meaning that we must delete the array that was generated by the classes constructor because it can only be full of junk data?

满是有效数据。您的对象已构造,其中包含一些数据,然后您将其他内容分配给该对象。


附录:什么时候调用复制构造函数,什么时候调用operator=(更多详细信息参见this question):

class A{};

int main()
{
    A a1; //default constructor 
    A a2 = a1; //copy-constructor, not assignment operator! New object is needed

    A a3; 
    a3 = a1; //we have valid and existing object on the left side, assignment operator is used
}

让我们从有关此代码段的问题开始。

name = new char[that.name.size()];
for (int i = 0; i < that.name.size(); i++)`
{
  name[i] = that.name[i]
}

数据成员名称的类型为 char *。那就是它是一个指针。指针和指针指向的字符都没有像size.

这样的成员函数

所以这段代码无效,无法编译。

你可以使用这样的方法

name = that.name;

如果数据成员的类型为 std::string

Why can't I just write: name = that.name

在这种情况下,class 的两个对象将包含指向相同内存范围的指针。因此,如果一个对象将被删除,那么另一个对象的指针将无效,并且使用它来删除分配的内存会导致未定义的行为。

When the assignment operator is invoked, is the regular constructor called immediately before the copy assignment operator function is called?

复制赋值运算符只能应用于已构造的对象。这是 class 的对象必须已经存在,并且使用的构造函数应通过 nullptr 或分配的内存地址初始化其数据成员名称。

I immediately shied away from this one because I couldn't understand why the function checks if(this != &that)

此检查可以避免对象的自我分配。也就是说,当一个对象被分配给它自己时,分配和删除内存的代码将是多余的。

What I'm finding confusing is, why are they including the line delete[] name;?

因为对象已经被构建并且它的数据成员名称可以指向包含名称字符串的分配内存。

考虑到上述所有内容,复制赋值运算符可能看起来像

person& operator =( const person &that )
{
    if ( this != &that )
    {
        char *local_name = new char[strlen(that.name) + 1];
        // If the above statement throws,
        // the object is still in the same state as before.
        // None of the following statements will throw an exception :)
        strcpy(local_name, that.name);
        delete[] name;
        name = local_name;
        age = that.age;
    }

    return *this;
}