微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++ Primer 第 5 版中发现的错误复制初始化与直接初始化

如何解决C++ Primer 第 5 版中发现的错误复制初始化与直接初始化

嗨,我想了解复制构造函数的工作原理并查看示例。示例如下:

int v[] = { 1,2,3 }; printf("%p\n",v);

我的问题是:

  1. 为什么写成“复制构造函数将 *p 复制到 {//new scope Sales_data *p = new Sales_data; auto p2 = make_shared<Saled_data>(); Sales_data item(*p); // copy constructor copies *p into item vector<Sales_data> vec; vec.push_back(*p2);// copies the object to which p2 points delete p; } ”?我的意思是,item 是直接初始化的。如果我们写 item 那么它会被称为复制初始化,那么为什么他们在注释中写了复制构造函数将 *p 复制到 item 中。

现在,为了自己验证这一点,我尝试自己创建一个简单的示例,但我也无法正确理解该概念。我的自定义示例如下:

Sales_data item = *p;

现在当我运行这个程序时,我得到以下输出

这是认初始化

这是直接初始化

这是直接初始化

我从这个程序中提出的问题如下:

  1. 为什么在第二种情况下,当我写 #include<iostream> #include<string> class MAINCLASS{ private: std::string name; int age =0; public: MAINCLASS(){ std::cout<<"This is default initialization"<<std::endl; } MAINCLASS(MAINCLASS &obj){ std::cout<<"This is direct initialization"<<std::endl; } MAINCLASS(const MAINCLASS &obj):name(obj.name),age(obj.age){ std::cout<<"This is copy initialization"<<std::endl; } }; int main(){ MAINCLASS objectone; MAINCLASS objecttwo =objectone; MAINCLASS objectthree(objectone); return 0; } 时,我们没有得到输出“这是复制初始化”?我已经读到在直接初始化函数匹配中使用,在复制构造函数中,我们将右手操作数成员复制到左手操作数成员中。因此,当我编写 MAINCLASS objecttwo =objectone; 时,它应该调用复制构造函数并在屏幕上打印“这是复制初始化”。但它是直接初始化对象。这里发生了什么?

解决方法

不要混淆复制构造和复制初始化。您可以使用直接或复制初始化进行复制构造。

Copy initialisation 指的是一组初始化语法和语义。这包括 T a = b 语法。

copy constructor 是一个特殊的类方法,它接受所述类的参数。这个方法应该只接受一个参数(T&const T& 都可以)。复制构造在调用该函数时发生。

考虑到这一点,我们可以继续回答您的问题。

  1. 为什么写成“复制构造函数将 *p 复制到 item”?我的意思是,item 是直接初始化的。如果我们写 Sales_data item = *p; 那么它将被称为复制初始化...

Sales_data item = *pSales_data item(*p) 都调用了复制构造函数。但是,前者使用复制初始化T a = b),而后者使用直接初始化T a(b))。

  1. 为什么在第二种情况下,当我写 MAINCLASS objecttwo =objectone; 时,我们没有得到输出“这是复制初始化”?

实际上,这里的问题不在于它是否被复制/直接初始化。这是左值/右值重载解析的问题。

考虑以下程序:

#include <iostream>

void f(int& i) { std::cout << "int&\n"; }
void f(const int& i) { std::cout << "const int&\n"; }

int main() {
    f(1); // f(const int&)
    
    int i = 2;
    f(i); // f(int&)
}

f 是根据传递的值是左值还是右值来选择的。在第一种情况下,1 是一个右值,因此调用 f(const int&)(参见 this)。在第二种情况下,i 是左值,选择 f(int&) 是因为它更通用。

因此,在您的情况下,MAINCLASS objecttwo =objectone;MAINCLASS objectthree(objectone); 都调用了复制构造函数。同样,前者使用复制初始化,而后者使用直接初始化。只是这两个调用都选择了非常量引用重载:MAINCLASS(MAINCLASS&)

,

尽管名称选择不当,复制初始化与复制构造函数是正交的。

复制构造函数是任何构造函数,其第一个参数是对其类类型的左值引用,并且可以只用一个参数调用。它只是一个构造函数,可以从现有对象初始化新对象。这就是它的全部内容。您声明的两个构造函数实际上都是复制构造函数。这个也太

MAINCLASS(MAINCLASS volatile &obj,void *cookie = nullptr) {
  // .. Do something
  // This is a copy c'tor since this is valid:
  // MAINCLASS volatile vo;
  // MAINCLASS copy1_vo(vo);
}

正如其他答案所指出的,复制初始化只是一系列初始化上下文的名称。它包括涉及 = 的初始化、将参数传递给函数、返回语句和 throw 表达式(我可能忘记了一些东西)。直接初始化涉及其他上下文。

复制构造函数可用于上述任何一种。无论是复制初始化还是直接初始化。两者之间的区别 - 属于构造函数 - 是如何构建构造函数的重载集。复制初始化不使用显式声明的构造函数。例如,在这个例子中

struct Example {
  Example() = default;
  explicit Example(Example const&) {}
};

int main() {
  Example e;
  Example e1(e); // Okay,direct initialization 
  Example e2 = e1; // Error! Copy initialization doesn't make use of explicit constructor
}

即使我们有一个复制构造函数,也不能在复制初始化上下文中调用它!


至于程序的意外打印,这只是重载决议选择更匹配函数的问题。您的原始对象未声明为 const。因此,将其绑定到非常量左值引用只是重载解析中的首选。

,

复制初始化和直接初始化是根据用于构造的语法。
Confusion in copy initialization and direct initialization

哪个构造函数被调用是基于重载解析(而不是构造的语法) 编译器调用与传递的参数与定义的参数最匹配的函数。

在您的示例中,由于 objectone 是非常量,最佳匹配是带有非常量参数的复制构造函数。由于另一个复制构造函数有一个 const& 参数,它将为 const 对象调用。

重写您的示例:

#include<iostream>
#include<string>

class MAINCLASS {
private:
    std::string name;
    int age = 0;
public:
    MAINCLASS() {
        std::cout << "This is default initialization" << std::endl;
    }
    MAINCLASS(MAINCLASS& obj) {
        std::cout << "This is copy constructor with non-const reference parameter" << std::endl;
    }
    MAINCLASS(const MAINCLASS& obj) :name(obj.name),age(obj.age) {
        std::cout << "This is copy constructor with const reference parameter" << std::endl;
    }
};

int main() {
    MAINCLASS objectone;
    const MAINCLASS const_objectone;

    MAINCLASS objecttwo = objectone;  // copy initialization of non-const object
    MAINCLASS objectthree(objectone); // direct initialization of non-const object

    MAINCLASS objectfour = const_objectone; // copy initialization of const object
    MAINCLASS objectfive(const_objectone);  // direct initialization of const object
    return 0;
}

输出将是:

This is default initialization
This is default initialization
This is copy constructor with non-const reference parameter
This is copy constructor with non-const reference parameter
This is copy constructor with const reference parameter
This is copy constructor with const reference parameter

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。