C++ Primer 第 5 版中发现的错误(复制初始化与直接初始化)
Error spotted in C++ Primer 5th Edition (copy intialization vs direct initialization)
您好,我正在尝试了解复制构造函数的工作原理并查看示例。示例如下:
{//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;
}
我的问题是:
- 为什么写成“copy constructor copies *p into
item
”?我的意思是,item
是直接初始化的。如果我们写 Sales_data item = *p;
那么它将被称为复制初始化,那么为什么他们在评论中写了复制构造函数 copies *p into item.
现在,为了自己验证这一点,我尝试自己创建一个简单的示例,但我也无法正确理解这个概念。我的自定义示例如下:
#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;
}
现在当我 运行 这个程序时,我得到以下输出:
This is defalut initialization
This is direct initialization
This is direct initialization
我从这个程序中得到的问题如下:
- 为什么在我写
MAINCLASS objecttwo =objectone;
时,在第二种情况下 没有得到 输出“这是复制初始化”?我读过在直接初始化函数中使用匹配,在复制构造函数中,我们将右手操作数成员复制到左手操作数成员中。所以当我写 MAINCLASS objecttwo =objectone;
时,它应该调用复制构造函数并在屏幕上打印“这是复制初始化”。但它是直接初始化对象。这里发生了什么?
复制初始化和直接初始化是根据构造时使用的语法
参见 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
不要混淆复制构造和复制初始化。您可以使用直接或复制初始化来复制构造。
Copy initialisation指的是一组初始化语法和语义。这包括 T a = b
语法。
copy constructor 是一种特殊的 class 方法,它采用上述 class 的参数。此方法应该只接受一个参数(T&
或 const T&
都可以)。复制构造发生在那个函数被调用的时候。
考虑到这一点,我们可以继续回答您的问题。
- Why it is written that "copy constructor copies *p into
item
"? I mean, item
is direct initialized. If we would have written Sales_data item = *p;
then it will be called copy initialized...
Sales_data item = *p
和Sales_data item(*p)
都调用了复制构造函数。但是,前者使用复制初始化(T a = b
),而后者使用直接初始化(T a(b)
)。
- Why are we not getting the output "this is copy initialization" in the second case when i write
MAINCLASS objecttwo =objectone;
?
其实这里的问题不在于copy/direct是否初始化。这是 lvalue/rvalue 重载解析的问题。
考虑以下程序:
#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&)
(请参阅 )。在第二种情况下,i
是左值,选择 f(int&)
因为它更通用。
所以在你的例子中,MAINCLASS objecttwo =objectone;
和 MAINCLASS objectthree(objectone);
都调用了 复制构造函数 。同样,前者使用复制初始化,而后者使用直接初始化。只是这两个调用都选择了非常量引用重载:MAINCLASS(MAINCLASS&)
.
尽管名称选择不当,但复制初始化与复制构造函数是正交的。
复制构造函数是任何构造函数,其第一个参数是对其 class 类型的左值引用,并且可以只用一个参数调用。它只是一个可以从现有对象初始化新对象的构造函数。这就是它的全部内容。您声明的两个构造函数实际上都是复制构造函数。这个也太
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);
}
正如其他答案所述,复制初始化只是一系列初始化上下文的名称。它包括涉及 =
的初始化、将参数传递给函数、return 语句和抛出表达式(我可能忘记了一些东西)。直接初始化涉及 other 个上下文。
拷贝构造函数可以用在上面的任何一个中。无论是复制初始化还是直接初始化。两者之间的区别——作为构造函数的附属物——是构造函数重载集的构建方式。复制初始化不使用显式声明的构造函数。例如,在这个例子中
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
}
即使我们有一个复制构造函数,也不能在复制初始化上下文中调用它!
就程序的意外打印而言,这只是选择更匹配函数的重载解析问题。您的原始对象未声明为常量。因此,将其绑定到非常量左值引用只是重载决议中的首选。
您好,我正在尝试了解复制构造函数的工作原理并查看示例。示例如下:
{//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;
}
我的问题是:
- 为什么写成“copy constructor copies *p into
item
”?我的意思是,item
是直接初始化的。如果我们写Sales_data item = *p;
那么它将被称为复制初始化,那么为什么他们在评论中写了复制构造函数 copies *p into item.
现在,为了自己验证这一点,我尝试自己创建一个简单的示例,但我也无法正确理解这个概念。我的自定义示例如下:
#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;
}
现在当我 运行 这个程序时,我得到以下输出:
This is defalut initialization
This is direct initialization
This is direct initialization
我从这个程序中得到的问题如下:
- 为什么在我写
MAINCLASS objecttwo =objectone;
时,在第二种情况下 没有得到 输出“这是复制初始化”?我读过在直接初始化函数中使用匹配,在复制构造函数中,我们将右手操作数成员复制到左手操作数成员中。所以当我写MAINCLASS objecttwo =objectone;
时,它应该调用复制构造函数并在屏幕上打印“这是复制初始化”。但它是直接初始化对象。这里发生了什么?
复制初始化和直接初始化是根据构造时使用的语法
参见 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
不要混淆复制构造和复制初始化。您可以使用直接或复制初始化来复制构造。
Copy initialisation指的是一组初始化语法和语义。这包括 T a = b
语法。
copy constructor 是一种特殊的 class 方法,它采用上述 class 的参数。此方法应该只接受一个参数(T&
或 const T&
都可以)。复制构造发生在那个函数被调用的时候。
考虑到这一点,我们可以继续回答您的问题。
- Why it is written that "copy constructor copies *p into
item
"? I mean,item
is direct initialized. If we would have writtenSales_data item = *p;
then it will be called copy initialized...
Sales_data item = *p
和Sales_data item(*p)
都调用了复制构造函数。但是,前者使用复制初始化(T a = b
),而后者使用直接初始化(T a(b)
)。
- Why are we not getting the output "this is copy initialization" in the second case when i write
MAINCLASS objecttwo =objectone;
?
其实这里的问题不在于copy/direct是否初始化。这是 lvalue/rvalue 重载解析的问题。
考虑以下程序:
#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&)
(请参阅 i
是左值,选择 f(int&)
因为它更通用。
所以在你的例子中,MAINCLASS objecttwo =objectone;
和 MAINCLASS objectthree(objectone);
都调用了 复制构造函数 。同样,前者使用复制初始化,而后者使用直接初始化。只是这两个调用都选择了非常量引用重载:MAINCLASS(MAINCLASS&)
.
尽管名称选择不当,但复制初始化与复制构造函数是正交的。
复制构造函数是任何构造函数,其第一个参数是对其 class 类型的左值引用,并且可以只用一个参数调用。它只是一个可以从现有对象初始化新对象的构造函数。这就是它的全部内容。您声明的两个构造函数实际上都是复制构造函数。这个也太
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);
}
正如其他答案所述,复制初始化只是一系列初始化上下文的名称。它包括涉及 =
的初始化、将参数传递给函数、return 语句和抛出表达式(我可能忘记了一些东西)。直接初始化涉及 other 个上下文。
拷贝构造函数可以用在上面的任何一个中。无论是复制初始化还是直接初始化。两者之间的区别——作为构造函数的附属物——是构造函数重载集的构建方式。复制初始化不使用显式声明的构造函数。例如,在这个例子中
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
}
即使我们有一个复制构造函数,也不能在复制初始化上下文中调用它!
就程序的意外打印而言,这只是选择更匹配函数的重载解析问题。您的原始对象未声明为常量。因此,将其绑定到非常量左值引用只是重载决议中的首选。