C++:从另一个构造函数隐式调用构造函数

C++: Implicit call of constructor from another constructor

我很难理解我正在处理的项目中的隐式构造函数调用。
有两个接口:InterfaceA和InterfaceB。
然后,有两个实现 classes:ImplementA 和 ImplementB,它们从各自的接口派生。在任何一点上都没有共同的祖先 - 尽管接口和 classes 非常相似。
第三个 class,Component,可用于初始化任何 Implement classes。
所以我们有这样的东西:

class InterfaceA 
{
public:
    InterfaceA(){}
    virtual ~InterfaceA(){}

    // Some functions
}

class InterfaceB 
{
public:
    InterfaceB(){}
    virtual ~InterfaceB(){}

    // Some functions
}

class ImplementA : public InterfaceA
{
public:
    ImplementA(Component& input);
    ImplementA(Component* input);
    ImplementA(const ImplementA& impl);
    ImplementA(const ImplementB& impl);
    ImplementA();
    ~ImplementA(void);

private:
    Component* m_component;
    bool some_boolean;
}

class ImplementB : public InterfaceB
{
public:
    ImplementB(Component& input);
    ImplementB(const ImplementA& impl);
    ImplementB(const ImplementB& impl);
    ImplementB();
    ~ImplementB(void);

private:
    Component* m_component;
}

然后,我们有一个 return 指向组件的指针的函数:

Component* foo()
{
    Component* result = new Component();
    ...
    return result;
}

最后,在代码的某处,我们有以下 return:

return new ImplementB(foo());

当运行时,上面的行执行foo(),然后执行ImplementA(Component* input),然后执行ImplementB(const ImplementA& impl) - 这让我很困惑。
1. 为什么它甚至可以编译?编译器不应该抱怨这种类型的参数没有有效的构造函数(当我在相关调用中更改某些内容时它会这样做)。顺便说一下,我使用的是 Visual Studio 2012。
2. 我知道当一个构造函数得到一个指向对象的指针作为参数时,它首先调用该参数的拷贝构造函数。所以,如果我忽略没有合适的构造函数这一事实——它不应该使用 Component 的复制构造函数而不是 ImplementA 吗? ImplementA 似乎与 ImplementB 完全没有联系(除了它们具有相似的结构)。

我错过了什么?在这种情况下可能导致此行为的原因是什么?

这就是你拥有太多、令人困惑的构造函数的结果。我也强烈怀疑你到处都有内存泄漏。

注意ImplementA有一个构造函数ImplementA(Component&)和一个构造函数ImplementA(Component*)。但是 ImplementB 只有 ImplementB(Component&)。这令人困惑。

所以当您执行 new ImplementB(foo()) 时,没有直接接受 Component* 的构造函数。编译器会寻找其他选项。具体来说,它会寻找一种方法将参数 Component* 转换为 ImplementB 的某些构造函数接受的参数。

C++ 有一个叫做用户定义转换的东西。您可以定义 conversion operatorsconverting constructors 来定义类型之间新的隐式类型转换。事情是这样的:任何具有单个参数的构造函数都是转换构造函数,除非它被标记为 explicit。 (这可能是 C++ 中的一个设计错误。但我们坚持了下来。)

因此,ImplementA(Component*) 定义了从 Component*ImplementA 的隐式转换。还有 ImplementB(const ImplementA&) 构造函数可以接受 ImplementA 临时的。所以编译器使用这个构造函数,使用转换构造函数来创建临时的,最终结果就是你看到的执行。

解决方案是制作所有这些构造函数 explicit,同时定义更少、更少混淆的构造函数。并使用智能指针来消除内存泄漏。

return new ImplementB(foo());

When run, the line above executes foo(), then executes ImplementA(Component* input), and then ImplementB(const ImplementA& impl) - which confuses me very much.

调用fooreturns一个Component*。当我们检查 ImplementB 的构造函数时,我们发现

ImplementB(Component& input);
ImplementB(const ImplementA& impl);
ImplementB(const ImplementB& impl);

所以其中 none 个接受 Component*

但是 "luckily" 其中一个选项是 ImplementA可以 从指针构造。

一个 "user-defined conversion"是允许传递参数的。

fooreturns一个Component*

ImplementB 没有接受 Component* 的构造函数。但是它有一个接受 const ImplementA&.

的构造函数

现在 Component* 可以通过接受 Component* 的后者的构造函数 隐式转换 ImplementA。因此它被转换,并且生成的 ImplementA 临时值通过 const 引用传递给 ImplementB.

的适当构造函数

如果您重视自己的理智,您可能不希望在代码附近的任何位置进行 任何 隐式转换。要停止用户定义类型的隐式转换,请声明所有单参数构造函数 explicit.