C++复制构造构造和分配问题

发布于 2024-08-25 02:53:26 字数 1927 浏览 8 评论 0原文

以下是《C++ Gotchas》一书第 56 条的摘录:

简单的情况并不罕见 写入Y对象的初始化 三种不同方式中的任何一种,就好像 它们是等价的。

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

事实上,这三个 初始化可能会导致 在相同的目标代码中 生成的,但它们并不等同。 a 的初始化被称为 a 直接初始化,确实如此 正是人们所期望的。这 初始化是通过完成 直接调用 Y::Y(int)。

b和c的初始化是 更复杂。其实他们也太 复杂的。这些都是复制品 初始化。在这种情况下 b 的初始化,我们请求 创建匿名临时对象 类型 Y,用值初始化 1066.然后我们使用这个匿名临时文件作为副本的参数 类 Y 的构造函数进行初始化 b.最后,我们调用析构函数 匿名临时工。

为了测试这一点,我做了一个带有数据成员的简单类(程序附在最后),结果令人惊讶。对于 c 的情况,似乎该对象是由复制构造函数构造的,而不是按照书中建议的那样。

有谁知道语言标准是否已更改或者这只是编译器的优化功能?我使用的是 Visual Studio 2008。

代码示例:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

输出:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

我对构建 d 和 e 的结果感到最惊讶。准确地说,我期望创建一个空对象,然后创建一个对象并将其分配给该空对象。实际上,对象是由复制构造函数创建的。

Here is an extract from item 56 of the book "C++ Gotchas":

It's not uncommon to see a simple
initialization of a Y object written
any of three different ways, as if
they were equivalent.

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

In point of fact, all three of these
initializations will probably result
in the same object code being
generated, but they're not equivalent.
The initialization of a is known as a
direct initialization, and it does
precisely what one might expect. The
initialization is accomplished through
a direct invocation of Y::Y(int).

The initializations of b and c are
more complex. In fact, they're too
complex. These are both copy
initializations. In the case of the
initialization of b, we're requesting
the creation of an anonymous temporary
of type Y, initialized with the value
1066. We then use this anonymous temporary as a parameter to the copy
constructor for class Y to initialize
b. Finally, we call the destructor for
the anonymous temporary.

To test this, I did a simple class with a data member (program attached at the end) and the results were surprising. It seems that for the case of c, the object was constructed by the copy constructor rather than as suggested in the book.

Does anybody know if the language standard has changed or is this simply an optimisation feature of the compiler? I was using Visual Studio 2008.

Code sample:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

Output:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

I was most surprised by the results of constructing d and e. To be precise, I was expecting an empty object to be created, and then an object to be created and assigned to the empty object. In practice, the objects were created by the copy constructor.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

玉环 2024-09-01 02:53:26

语法

X a = b;

a 和 b 属于 X 类型的 始终意味着复制构造。无论使用什么变体,例如:

X a = X();

,都没有进行任何分配,而且从来没有进行过。构造和分配类似于:

X a;
a = X();

The syntax

X a = b;

where a and b are of type X has always meant copy construction. Whatever variants, such as:

X a = X();

are used, there is no assignment going on, and never has been. Construct and assign would be something like:

X a;
a = X();
赴月观长安 2024-09-01 02:53:26

编译器可以将 bc 情况优化为与 a 相同。此外,无论如何,编译器都可以完全消除复制构造和赋值运算符调用,因此您看到的任何内容都不一定与不同的编译器甚至编译器设置相同。

The compiler is permitted to optimize cases b and c to be the same as a. Further, copy construction and assignment operator calls can be wholly eliminated by the compiler anyway, so whatever you see isn't necessarily the same with different compilers or even compiler settings.

内心荒芜 2024-09-01 02:53:26

从 C++17 开始,这三个都是等效的(除非 Y::Y(int)显式,这将简单地禁止c),因为通常称为强制复制省略。

即使 Y c = 1066; 也仅创建一个 Y 对象,因为隐式转换为 Y 的结果是一个用于初始化的纯右值c 而不是创建一个临时的。

As of C++17, all three of these are equivalent (unless Y::Y(int) is explicit, which would simply disallow c) because of what is often called mandatory copy elision.

Even Y c = 1066; creates only the one Y object because the result of the implicit conversion to Y is a prvalue that is used to initialize c rather than to create a temporary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文