C++ 中的复制构造函数和赋值运算符

Copy Constructors And Assignment Operator in C++

提问人:alexmoran 提问时间:3/1/2020 最后编辑:David C. Rankinalexmoran 更新时间:3/1/2020 访问量:804

问:

当我在 C++ 中学习构造函数时,我想到了对我来说很难完全理解的东西。一种是当我们创建一个类的对象时(比如说),如果我们没有为自己编写构造函数,则会调用默认构造函数。(我认为复制构造函数也一样)class Point

Point p2 = p1;

在这种情况下,我得到了默认复制构造函数的调用,但是如果我这样做怎么办:

Point p1;
p1.setX(3);
p2.setY(2);

Point p2;

p2 = p1;

这仍然有效,但我想默认的复制构造函数不会在这里被调用,因为我认为构造函数在创建对象时被调用。那么,在第二种情况下,该任务如何工作呢?

除此之外,我想知道我们什么时候编写自己的复制构造函数(左右)。下面的两个例子之间有区别还是只是句法糖?ClassName (const ClassName &old_obj);

Point p3(p1);

Point p3 = p1;

在第二个例子中,为什么程序推断我们作为参数传递给复制构造函数,而我们没有在 inside parantheses 之后编写它?p1p3

此外,当我们创建一个类的对象,该对象将参数用于其结构时,我们使用括号将数据作为参数传递。但是当涉及到默认构造函数时(即使我们编写了自己的构造函数),我们根本不使用括号并创建这样的对象:

Point p1;

而不是那样的:

Point p1(); // even if we defined our default constructor like "Point() {}"

这是什么原因?程序是否知道不要为我们创建默认构造函数?

C++ 复制构造函数 default-constructor

评论

1赞 AVH 3/1/2020
最后一个示例不是默认构造函数,它是调用的函数的函数声明,该函数不带参数并返回 .因此,这就是为什么你不能用它来创建一个被调用的.这被称为最令人烦恼的解析p1PointPointp1
1赞 David C. Rankin 3/1/2020
查看 Default 构造函数Copy 构造函数Member initialization。不,对于新程序员来说,材料所呈现的技术性质并不总是最容易消化的(别担心,它变得更容易)。对于最后一点(没有双关语的意思),声明一个实例 ,同时创建一个函数。Point p1;class PointPoint pi();
0赞 alexmoran 3/1/2020
哦,好吧,最后一个声明了一个返回类型的函数。我也会看看这些链接。Point
0赞 Ted Lyngmo 3/1/2020
@alexmoran还要注意,定义一个构造函数可能会自动删除其他构造函数的自动存在。

答:

1赞 dmeister 3/1/2020 #1

我在这里假设一个默认的 Point 实现。

当我在 C++ 中学习构造函数时,我想到了对我来说很难完全理解的东西。一种是当我们创建一个类的对象(比如类 Point)时,如果我们没有为自己编写构造函数,则会调用默认构造函数。(我认为复制构造函数也一样)

点 p2 = p1;

在这种情况下,我得到了默认复制构造函数的调用,但是如果我这样做怎么办:

点 p1; p1.setX(3); p2.setY(2);

点 p2;

p2 = p1; 这仍然有效,但我想默认的复制构造函数不会在这里被调用,因为我认为构造函数在创建对象时被调用。那么,在第二种情况下,该任务如何工作呢?

真。使用赋值运算符。复制构造函数仅在创建新的 Point 对象时使用。在实践中,如果您有一个复制构造函数,则应该有一个带有 Machting 实施。

除此之外,我想知道我们什么时候编写自己的复制构造函数(ClassName (const ClassName &old_obj);下面的两个例子之间有区别还是只是句法糖?

如果有什么特别之处,您可以编写自己的复制构造函数。也许由于某种原因不应该复制成员。也许成员不能使用复制构造函数/赋值构造函数进行复制,并且您必须调用非标准的 CloneThis 方法。 如果所有成员都是可复制的,并且所有成员都应该被复制,则只需使用 = default。

(我将把搬家任务/搬家施工排除在外)

点 p3(p1); 和

点 p3 = p1; 在第二个示例中,为什么程序推断我们将 p1 作为参数传递给复制构造函数,而我们没有将其写在 p3 之后?

由于C++11,它保证是句法糖。它们都保证调用复制构造函数。另一种解释是首先调用 p3 的默认构造函数,然后调用赋值运算符将 p1 的值分配给 p3。这显然是浪费的,如果 p3 不是默认构造的,甚至可能是不可能的。

此外,当我们创建一个类的对象,该对象将参数用于其结构时,我们使用括号将数据作为参数传递。但是当涉及到默认构造函数时(即使我们编写了自己的构造函数),我们根本不使用括号并创建这样的对象:

点 p1; 而不是那样的:

点 p1();即使我们定义了默认构造函数,例如 “Point() {}” 这是什么原因?程序是否知道不要为我们创建默认构造函数?

你可以写 Point p1;或者你可以写 Point p1{};

你可以写 Point p1(),但这意味着声明一个没有参数的函数 p1,它返回一个 Point。 但是,现代编译器会向您发出有关第三种变体的警告,因为这是一个常见的错误。

现代建议使用 {},因为它更统一。 点 p1{}; 点 p2{1, 1};

希望这会有所帮助。

评论

0赞 alexmoran 3/1/2020
这很有帮助,但我试图理解的一件事是:当我们尝试分配我们之前创建的两个类对象时,CPP 能够理解我们是在告诉它将变量值一个接一个地复制到另一个?(如果对象已经创建,并且没有调用复制构造函数?
0赞 dmeister 3/2/2020
我不确定我是否理解:默认情况下生成的复制构造函数/复制赋值会逐个复制成员。
0赞 alexmoran 3/3/2020
我的问题是,赋值运算符不仅用于原始数据类型。如果它与类对象等一起使用,复制赋值运算符会吗?
0赞 dmeister 3/6/2020
这样想吧。C++ 努力使添加感觉“原始”的新类型变得容易,例如带有赋值的字符串类、复数类(在标准中)或类无限域整数。所有对用户来说都应该感觉很自然。这就是为什么一般运算符重载在 C++ 中是一回事。一般而言,值语义(又名赋值运算符和构造函数)在 C++11/14/17 中变得更加重要。值语义通常比处理指针和引用更安全,而且通常不会(太多)昂贵。