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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C ++:从另一个构造函数隐式调用构造函数相关的知识,希望对你有一定的参考价值。

我正在努力理解我正在研究的项目中的隐式构造函数调用。 有两个接口:InterfaceA和InterfaceB。 然后,有两个实现类:ImplementA和ImplementB,它们派生自相应的接口。在任何方面都没有共同的祖先 - 即使接口和类非常相似。 第三个类Component可用于初始化任何实现类。 所以我们有这样的事情:

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

然后,我们有一个返回指向Component的指针的函数:

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

最后,在代码中的某个地方,我们得到以下回报:

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 ++有一种称为用户定义的转换。您可以定义转换运算符和转换构造函数,以定义类型之间的新隐式类型转换。事情是这样的:任何带有单个参数的构造函数都是转换构造函数,除非它用explicit标记。 (这可能是C ++中的一个设计错误。但我们坚持使用它。)

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

解决方案是使所有这些构造函数explicit,并且还定义更少,更少混淆的构造函数。并使用智能指针摆脱内存泄漏。

另一答案
return new ImplementB(foo());

运行时,上面的行执行foo(),然后执行ImplementA(Component * input),然后执行ImplementB(const ImplementA&impl) - 这让我非常困惑。

foo的召唤返回了Component*。当我们检查ImplementB的构造函数时,我们发现

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

所以这些都不接受Component*

但“幸运的是”其中一个选项是ImplementA,它可以用指针构造。

传递参数时允许一个“用户定义的转换”。

另一答案

foo返回Component*

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

现在Component*可以通过接受ImplementA的后者的构造函数隐式转换为Component*。所以它被转换,结果ImplementA临时通过const引用传递给ImplementB的相应构造函数。

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

以上是关于C ++:从另一个构造函数隐式调用构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何从另一个构造函数调用 C++ 类构造函数 [重复]

如何在 Java 中从另一个构造函数调用一个构造函数?

在java中从另一个调用一个构造函数[重复]

在破坏调用期间从另一个线程未定义的行为调用对象上的方法?

如何在 Java 中从另一个构造函数调用一个构造函数?

C/C++编程笔记:高级C++知识 |虚拟副本构造器