我很难理解我正在进行的项目中的隐式构造函数调用。
有两个接口: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完全没有联系(除了它们具有类似的结构)。
我错过了什么?在这种情况下可能导致这种行为的原因是什么?
答案 0 :(得分:5)
这是因为拥有太多令人困惑的构造函数而获得的。我也强烈怀疑你到处都有内存泄漏。
请注意,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
,并定义更少,更少混淆的构造函数。并使用智能指针来摆脱内存泄漏。
答案 1 :(得分:1)
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
,可以从指针构建。
传递参数时,允许一个“用户定义的转换”。
答案 2 :(得分:0)
foo
返回Component*
。
ImplementB
没有接受Component*
的构造函数。但是它有一个接受const ImplementA&
的构造函数。
现在Component*
通过接受ImplementA
的后者的构造函数隐式转换到Component*
。所以它被转换,结果ImplementA
临时通过const引用传递给ImplementB
的相应构造函数。
如果您重视自己的理智,那么您可能不希望代码附近的任何隐式转化。要停止对用户定义类型的隐式转换,请声明所有单参数构造函数explicit
。