构造函数混淆
Constructor confusion
我一直认为我非常了解C++,但有时我甚至对最基本的东西感到惊讶。
在以下场景中,我对调用构造函数Derived::Derived(const Base&)
的原因感到困惑:
class Base
{ };
class Derived : public Base
{
public:
Derived() { }
Derived(const Base& b)
{
std::cout << "Called Derived::Derived(const Base& b)" << std::endl;
}
};
int main()
{
Derived d;
Base b;
d = b;
}
它输出:Called Derived::Derived(const Base& b)
,表示调用了Derived
中的第二个构造函数。现在,我认为我非常了解C++,但我不明白为什么要调用那个构造函数。我理解整个"四条规则"概念,我认为表达式d = b
可以做两件事之一:要么1)调用Base
的隐式(编译器生成的)赋值运算符,要么2)触发编译器错误,抱怨函数Derived& operator = (const Base&)
不存在。
相反,它调用了构造函数,即使表达式d = b
是赋值表达式。
那么为什么会发生这种情况呢?
d=b可能发生,因为b已转换为Derived。第二个构造函数用于自动类型转换。这就像d=(派生)b
Derived是Base,但Base不是Derived,因此必须在赋值前对其进行转换。
将基分配给派生?也许你指的是参考文献(b)中的(a),或者衍生自base。这实际上没有意义,但编译器正确地使用了您的(非显式)构造函数将Base实例转换为新的Derived实例(随后将其赋值为d)。
使用显式构造函数可以防止这种情况自动发生。
就我个人而言,我认为您把代码示例搞砸了,因为如果没有转换
这里有两个交互功能:
- 赋值运算符从不继承
- 非显式构造函数或转换运算符(
operator T()
)定义了可以隐式用作转换序列一部分的用户转换
分配运算符从不继承
一个简单的代码示例:
struct Base {}; // implicitly declares operator=(Base const&);
struct Derived: Base {}; // implicitly declares operator=(Derived const&);
int main() {
Derived d;
Base b;
d = b; // fails
}
来自视频:
prog.cpp: In function ‘int main()’:
prog.cpp:7: error: no match for ‘operator=’ in ‘d = b’
prog.cpp:2: note: candidates are: Derived& Derived::operator=(const Derived&)
转换序列
每当出现"阻抗"不匹配时,例如:
Derived::operator=
需要Derived const&
参数- 提供CCD_ 11
编译器将尝试建立一个转换序列来弥补这一差距。这样的转换序列最多可包含一个用户定义的转换。
在这里,它将寻找:
- 可以用
Base&
调用的Derived
的任何构造函数(非显式) Base
中会产生Derived
项的转换运算符
没有Base::operator Derived()
,但有一个Derived::Derived(Base const&)
构造函数。
因此,我们的转换序列是为我们定义的:
Base&
Base const&
(琐碎)Derived
(使用Derived::Derived(Base const&)
)Derived const&
(绑定到常量引用的临时对象)
然后调用CCD_ 23。
正在运行
如果我们用更多的跟踪来扩充代码,我们可以看到它在运行。
#include <iostream>
struct Base {}; // implicitly declares Base& operator(Base const&);
struct Derived: Base {
Derived() {}
Derived(Base const&) { std::cout << "Derived::Derived(Base const&)n"; }
Derived& operator=(Derived const&) {
std::cout << "Derived::operator=(Derived const&)n";
return *this;
}
};
int main() {
Derived d;
Base b;
d = b;
}
哪个输出:
Derived::Derived(Base const&)
Derived::operator=(Derived const&)
注意:防止这种情况发生
在C++中,可以删除转换序列中使用的构造函数。为此,需要使用explicit
关键字作为构造函数声明的前缀。
在C++0x中,也可以在转换运算符(operator T()
)上使用此关键字。
如果在这里我们在Derived::Derived(Base const&)
之前使用explicit
,那么代码就会变得格式错误,应该被编译器拒绝。
由于您已经为Derived定义了一个采用Base类型的构造函数,并且您正在向下转换Base,因此编译器会为向上转换选择最合适的构造函数,在本例中,它就是您定义的Dervied(const Base&b)。如果您没有定义此构造函数,那么在尝试进行赋值时,实际上会出现编译错误。有关更多信息,您可以在Linuxtopia上阅读以下内容。
它不能分配不同类型的值,所以它应该首先构造一个Derived
临时值。
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 具有默认模板类型的默认构造函数的类型推导
- 使用dynamic_cast和构造函数时出错
- 在c++中使用向量时,如何调用构造函数和析构函数
- 奇怪的构造函数行为