嘿,这是一个非常基本的问题,但我对此感到困惑。假设我创建了一个对象
MyObject a
.
它带有一个复制构造函数,所以我知道我可以这样做:
MyObject b(a);
但我可以这样做吗?
MyObject& b(a);
如果我这样做:
MyObject b = a;
里面有什么b
?如果这个问题太基本而无法发布,请道歉。
嘿,这是一个非常基本的问题,但我对此感到困惑。假设我创建了一个对象
MyObject a
.
它带有一个复制构造函数,所以我知道我可以这样做:
MyObject b(a);
但我可以这样做吗?
MyObject& b(a);
如果我这样做:
MyObject b = a;
里面有什么b
?如果这个问题太基本而无法发布,请道歉。
做MyObject& b(a)
与复制构造函数无关。它只是创建b
了对 object 的引用a
。没有任何东西被复制。将b
其视为对象的别名a
。从那时起,您可以等效地使用b
and来引用同一个对象。a
MyObject b = a;
将使用复制构造函数,就像MyObject b(a);
会一样。
初始化有两种形式:T x = a;
称为复制初始化;T x(a)
并且T x{a}
被称为直接初始化。
何时T
是引用类型,使用哪种类型的初始化并不重要。两者具有相同的效果。
当T
是类类型时,我们有两种可能:
如果初始化是直接初始化(MyClass b(a);
_ _ _ _a
T
MyClass b = a;
T
如您所见,您的两个示例都属于此类类型的初始化程序。
如果初始化是任何其他形式的复制初始化,则将考虑任何用户定义的转换序列,然后是直接初始化。用户定义的转换序列基本上是任何标准转换序列,其中包含一个转换构造函数。
如果c
是Foo
类类型并且有一个从Foo
to的转换构造函数MyClass
,那么MyClass b = c;
将等价于MyClass b(MyClass(c));
.
所以基本上,如果源类型和目标类型相同,那么两种初始化形式都是等价的。如果需要转换,则不需要。一个简单的例子来说明这一点:
#include <iostream>
struct Bar { };
struct Foo
{
Foo(const Foo& f) { std::cout << "Copy" << std::endl; }
Foo(const Bar& b) { std::cout << "Convert" << std::endl; }
};
int main(int argc, const char* argv[])
{
Bar b;
Foo f1(b);
std::cout << "----" << std::endl;
Foo f2 = b;
return 0;
}
该程序的输出(禁用复制省略)是:
Convert
----
Convert
Copy
当然,还有很多其他类型的初始化(列表初始化、字符数组、聚合等)。
这是我的看法:
引用与其他人的存储相关联,每当您访问引用时,您都在访问该存储。引用不能分配给 null 因为它们只是一个别名。引用必须在创建时进行初始化。(指针可以随时初始化。)
所以当你说
MyObject& b(a);
编译器分配一块存储 b,并将引用绑定到 a。
当你说
MyObject b = a;
您将 a 的引用传递给复制构造函数并从中创建新的 b 。请注意,仅当您为其编写了复制构造函数时,它才是深层副本。否则它会调用默认的复制构造函数,这会导致浅拷贝。
当你说
a = b; // after creating these objects
它翻译为 Object::operator=(const Object&),因此 A.operator=(B) 被调用(调用简单复制,而不是复制构造函数!)