我正在使用GCC 4.5并且观察到非常特殊的行为。我想知道这个运算符是否有什么我不完全理解的东西。我以为我精通C ++。
我有一个用于Windows Wnd
对象的精简C ++包装类HWND
,它具有已实现的强制转换运算符operator HWND ...
。
如果我使用这样的条件运算符(给定输入Wnd *p
和样本函数SetParent(HWND))
:
SetParent((p!=NULL) ? (HWND)(*p) : NULL)
父母已正确设置为NULL
或p
。这就是我所期待的。
但是,如果敢于懒惰并写下:
SetParent(p ? *p : NULL)
事情变得混乱。
运行GDB后,我发现在调用p
之后,变量SetParent
上会调用析构函数。
任何想法在这里发生了什么?
修改 这是我的Wnd课程:
class Wnd{
HWND m_hwnd; ///< the actual handle
WndFake *fake; ///< store state here if we do not have a handle
public:
virtual ~Wnd();
//contructor s
Wnd(HWND wnd=NULL):m_hwnd(wnd),fake(NULL){}
Wnd(DWORD sty,const jchar *title,const RECT &sz);
operator HWND(){return m_hwnd;}
operator HWND() const {return m_hwnd;}
}
答案 0 :(得分:4)
我怀疑你的Wnd也有非显式转换构造函数,它需要HWND甚至是int? 如果是,那么明确说明。
您的Wnd可能没有复制构造函数和operator =声明?声明这些是私有的,不要定义它们。
同时删除operator HWND
并向您的Wnd添加成员函数HWND hwnd() const;
。然后代码看起来像:
Setparent( p ? p->hwnd() : NULL );
我相信,当这些mod完成后,你会发现你的Wnd有什么问题。
问题表明了,因为两个操作数:in?:必须是相同的类型,因此NULL(0)可以某种方式转换为Wnd。因此* p的临时副本被作为返回值?:然后调用操作符HWND。
答案 1 :(得分:3)
析构函数是在变量p上调用还是在某个临时变量上调用p?
的副本在您的第一个示例中,您使用c样式转换将*p
转换为HWND
。在第二种情况下,您让编译器进行转换,这可能涉及制作*p
的副本。
答案 2 :(得分:2)
必须将?:
运算符的操作数设置为通用类型,这将在结果中使用。当你使用
SetParent(p != NULL ? (HWND) *p : NULL);
当编译器选择公共类型为HWND
时,你基本上手动强制执行这种情况。即以上变体等同于
SetParent(p != NULL ? (HWND) *p : (HWND) NULL);
但是当你做的时候
SetParent(p != NULL ? *p : NULL);
语言规则的工作方式不同,编译器将以不同的方式决定常用类型。在此,常见类型不是HWND
,而是Wnd
。两个操作数都转换为Wnd
,后一个变体被解释为
SetParent(p != NULL ? *p : Wnd(NULL));
即。当p
为null时,编译器构造一个临时对象Wnd(NULL)
(使用您提供的转换构造函数)并将其作为结果返回。而且,编译器很可能也会在true分支中构造一个临时对象(通过使用复制构造函数)。然后,生成的临时对象将转换为HWND
类型(因为这是SetParent
所需的),因此整个事物被解释为
SetParent((HWND) (p != NULL ? Wnd(*p) : Wnd(NULL)));
然后在调用SetParent
后立即销毁临时对象。这是您观察到的破坏,除非您错误地将其解释为p
的破坏。
编译器可以选择此方法的原因是因为您的转换构造函数未声明explicit
。如果您声明转换构造函数explicit
class Wnd {
...
explicit Wnd(HWND wnd=NULL) : m_hwnd(wnd), fake(NULL) {}
...
};
编译器将无法再使用隐式转换NULL
到Wnd
。在这种情况下,编译器将别无选择,只能使用HWND
作为通用类型
SetParent(p != NULL ? (HWND) *p : (HWND) NULL);
就像你想要的那样。
P.S。当您的班级中已经有operator HWND() const
时,实施相同的非常量版本operator HWND()
毫无意义。