简体   繁体   中英

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.
Then, there are two implementation classes: ImplementA and ImplementB, which are derived from the respective interface. 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.
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* 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.
1. Why does it even compile? 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.
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. So, if I ignore the fact that there's no fitting constructor - shouldn't it use the copy constructor of Component instead of ImplementA? ImplementA seems to have absolutely no connection to ImplementB (aside from them having a similar structure).

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*) . But ImplementB has only ImplementB(Component&) . This is confusing.

So when you do new ImplementB(foo()) , there's no constructor that accepts a Component* directly. 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.

C++ has something called user-defined conversions. 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 . (This is probably a design mistake in C++. But we're stuck with it.)

Therefore, ImplementA(Component*) defines an implicit conversion from Component* to ImplementA . And there's the ImplementB(const ImplementA&) constructor that would accept a ImplementA temporary just fine. 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. 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.

The call to foo returns a Component* . When we check the constructors for ImplementB we find

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

So none of these accepts a Component* .

But "luckily" one of the options is an ImplementA which can be constructed from the pointer.

And one "user-defined conversion" is allowed when passing a parameter.

foo returns a Component* .

ImplementB doesn't have a constructor that accepts a Component* . However it has a constructor that accepts a const ImplementA& .

Now Component* is implicitly convertible to ImplementA via the constructor of the latter that accepts a Component* . So it gets converted, and the resulting ImplementA temporary is passed by const reference to the appropriate constructor of 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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