简体   繁体   English

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

[英]C++: Implicit call of constructor from another constructor

I'm having a hard time understanding implicit constructor calls in a project I'm working on. 我很难理解我正在处理的项目中的隐式构造函数调用。
There are two interfaces: InterfaceA and InterfaceB. 有两个接口:InterfaceA和InterfaceB。
Then, there are two implementation classes: ImplementA and ImplementB, which are derived from the respective interface. 然后,有两个实现类:ImplementA和ImplementB,它们是从各自的接口派生的。 There is no mutual ancestor in any point - even though the interfaces and classes are very much alike. 尽管接口和类非常相似,但任何时候都没有共同的祖先。
A third class, Component, can be used to initialize any of the Implement classes. 第三类Component可用于初始化任何Implement类。
So we have something like this: 所以我们有这样的事情:

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

Then, we have a function that returns a pointer to Component: 然后,我们有一个函数返回一个指向Component的指针:

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

And finally, somewhere in the code, we have the following return: 最后,在代码中的某个地方,我们得到以下返回:

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. 运行时,上面的代码行执行foo(),然后执行ImplementA(Component * input),然后执行ImplementB(const ImplementA&impl)-这使我非常困惑。
1. Why does it even compile? 1.为什么还要编译? Shouldn't the compiler complain that there is no valid constructor for this type of argument (which it does when I change something in the call in question). 编译器不应该抱怨这种类型的参数没有有效的构造函数(当我在有问题的调用中更改某些内容时,它就会这样做)。 I'm using Visual Studio 2012, by the way. 顺便说一下,我正在使用Visual Studio 2012。
2. I know that when a constructor gets a pointer to an object as an argument, it first calls the copy constructor of the argument. 2.我知道,当构造函数获取指向对象的指针作为参数时,它首先调用该参数的副本构造函数。 So, if I ignore the fact that there's no fitting constructor - shouldn't it use the copy constructor of Component instead of ImplementA? 因此,如果我忽略了没有合适的构造函数的事实-它不应该使用Component的复制构造函数而不是ImplementA吗? ImplementA seems to have absolutely no connection to ImplementB (aside from them having a similar structure). ImplementA似乎与ImplementB绝对没有联系(除了它们具有相似的结构)。

What am I missing? 我想念什么? What could be causing this behavior in this situation? 在这种情况下,什么可能导致此行为?

This is what you get for having too many, confusing constructors. 这就是您拥有太多令人困惑的构造函数的结果。 I also strongly suspect that you have memory leaks all over the place. 我也强烈怀疑您到处都有内存泄漏。

Note that ImplementA has a constructor ImplementA(Component&) and a constructor ImplementA(Component*) . 请注意, ImplementA具有构造函数ImplementA(Component&)和构造函数ImplementA(Component*) But ImplementB has only ImplementB(Component&) . 但是ImplementB仅具有ImplementB(Component&) This is confusing. 这很混乱。

So when you do new ImplementB(foo()) , there's no constructor that accepts a Component* directly. 因此,当您执行new ImplementB(foo()) ,没有构造函数直接接受Component* The compiler goes looking for other options. 编译器会寻找其他选项。 Specifically, it goes looking for a way to convert the argument Component* to something that some constructor of ImplementB accepts. 具体来说,它正在寻找一种将参数Component*转换为ImplementB某些构造函数所接受的方法。

C++ has something called user-defined conversions. C ++有一些称为用户定义的转换的东西。 You can define conversion operators and converting constructors that define new implicit type conversions between types. 您可以定义转换运算符转换构造函数 ,以定义类型之间的新隐式类型转换。 Here's the thing: any constructor with a single parameter is a converting constructor, unless it is marked with explicit . 事情是这样的:任何具有单个参数的构造函数都是转换构造函数,除非它被显explicit标记。 (This is probably a design mistake in C++. But we're stuck with it.) (这可能是C ++中的一个设计错误。但是我们坚持下去。)

Therefore, ImplementA(Component*) defines an implicit conversion from Component* to ImplementA . 因此, ImplementA(Component*)定义了从Component*ImplementA的隐式转换。 And there's the ImplementB(const ImplementA&) constructor that would accept a ImplementA temporary just fine. 还有一个ImplementB(const ImplementA&)构造函数,可以接受一个临时的ImplementA So the compiler uses this constructor, uses the converting constructor to create the temporary, and the end result is the execution you're seeing. 因此,编译器使用此构造函数,使用转换的构造函数创建临时结构,最终结果是您看到的执行。

The solution is to make all these constructors explicit , and also to define fewer, less confusing constructors. 解决方案是使所有这些构造函数都是explicit ,并定义更少,更少混淆的构造函数。 And to use smart pointers to get rid of the memory leaks. 并使用智能指针摆脱内存泄漏。

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. 运行时,上面的代码行执行foo(),然后执行ImplementA(Component * input),然后执行ImplementB(const ImplementA&impl)-这使我非常困惑。

The call to foo returns a Component* . foo的调用返回Component* When we check the constructors for ImplementB we find 当我们检查ImplementB的构造函数时,我们发现

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

So none of these accepts a Component* . 因此,这些都不接受Component*

But "luckily" one of the options is an ImplementA which can be constructed from the pointer. 但是“幸运的”选项之一是可以从指针构造的ImplementA

And one "user-defined conversion" is allowed when passing a parameter. 传递参数时,允许一个 “用户定义的转换”。

foo returns a Component* . foo返回一个Component*

ImplementB doesn't have a constructor that accepts a Component* . ImplementB没有接受Component*的构造函数。 However it has a constructor that accepts a const ImplementA& . 但是,它有一个接受const ImplementA&const ImplementA&构造const ImplementA&

Now Component* is implicitly convertible to ImplementA via the constructor of the latter that accepts a Component* . 现在, Component* 通过接受Component*的后者的构造函数隐式转换ImplementA So it gets converted, and the resulting ImplementA temporary is passed by const reference to the appropriate constructor of ImplementB . 因此,它将进行转换,并且通过const引用将所得的ImplementA临时变量传递给ImplementB的适当构造函数。

If you value your sanity, you probably don't want any implicit conversions anywhere near your code. 如果您重视理智,则可能不希望在代码附近的任何地方进行任何隐式转换。 To stop implicit conversions of user-defined types, declare all single-argument constructors explicit . 要停止用户定义类型的隐式转换,请将所有单参数构造函数explicit

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM