首页 > 解决方案 > C++ Primer 第 5 版中发现的错误(复制初始化与直接初始化)

问题描述

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

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

我的问题是:

  1. 为什么写“复制构造函数将 *p 复制到item”?我的意思是,item是直接初始化的。如果我们会写,Sales_data item = *p;那么它将被称为复制初始化,所以他们为什么要写复制构造函数将 *p 复制到注释中的项目中。

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

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

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

这是默认初始化

这是直接初始化

这是直接初始化

我对这个程序的问题如下:

  1. 为什么在我写的第二种情况下我们没有得到输出“这是复制初始化” MAINCLASS objecttwo =objectone;?我已经读过,在直接初始化函数匹配中使用了复制构造函数,我们将右手操作数成员复制到左手操作数成员中。所以当我写MAINCLASS objecttwo =objectone;它应该调用复制构造函数并在屏幕上打印“这是复制初始化”。但相反,它是直接初始化对象。这里发生了什么?

标签: c++c++11c++14copy-constructor

解决方案


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

复制初始化是指一组初始化语法和语义。这包括T a = b语法。

复制构造函数是一种特殊的类方法,它接受所述类的参数。这个方法应该只接受一个参数(两者都做T&const T&将做)。调用该函数时会发生复制构造

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

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

两者都Sales_data item = *p调用Sales_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);复制构造函数。同样,前者使用复制初始化,而后者使用直接初始化。只是这两个调用都选择了非常量 ref 重载:MAINCLASS(MAINCLASS&).


推荐阅读