C++ 条件运算符的限制?:
我正在使用GCC4.5,并且观察到了非常奇怪的行为。我想知道这个操作员是否有什么我不完全理解的地方。我认为我精通C++。 我有一个瘦的C++包装类<代码> WND <代码> > Windows >代码> HWND < /Cord>对象,其中有一个已实现的CAST运算符<代码>操作符HWND…< /Cord> 如果我像这样使用条件运算符(给定输入C++ 条件运算符的限制?:,c++,conditional,destructor,operator-keyword,conditional-operator,C++,Conditional,Destructor,Operator Keyword,Conditional Operator,我正在使用GCC4.5,并且观察到了非常奇怪的行为。我想知道这个操作员是否有什么我不完全理解的地方。我认为我精通C++。 我有一个瘦的C++包装类 WND > Windows >代码> HWND < /Cord>对象,其中有一个已实现的CAST运算符操作符HWND…< /Cord> 如果我像这样使用条件运算符(给定输入Wnd*p和示例函数SetParent(HWND)): 父项正确设置为NULL或p,具体取决于。这就是我所期望的。 但是如果你敢偷懒,写: SetParent(p ? *p :
Wnd*p
和示例函数SetParent(HWND))
:
父项正确设置为NULL
或p
,具体取决于。这就是我所期望的。
但是如果你敢偷懒,写:
SetParent(p ? *p : NULL)
事情变得一团糟。
在运行GDB之后,我发现在调用SetParent
之后,对变量p
调用了析构函数。
你知道这是怎么回事吗
编辑
这是我的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;}
}
class-Wnd{
HWND m_HWND;//<实际句柄
WndFake*fake;//<如果我们没有句柄,请在此处存储状态
公众:
虚拟~Wnd();
//承包商
Wnd(HWND-Wnd=NULL):m_-HWND(Wnd),fake(NULL){}
Wnd(DWORD sty、const jchar*title、const RECT&sz);
运算符HWND(){return m_HWND;}
运算符HWND()常量{return m_HWND;}
}
是在变量p上调用析构函数,还是在某个临时变量上调用析构函数,该临时变量是p的副本
在第一个示例中,您使用c样式转换将
*p
转换为HWND
。在第二种情况下,您让编译器进行转换,这可能涉及到制作*p
的副本。我怀疑您的Wnd也有非显式转换构造函数,它接受HWND甚至int?
如果是这样的话,那么就把它说清楚
您的Wnd可能没有声明复制构造函数和运算符=吗?将这些声明为私有,而不定义它们
同时删除操作符HWND
并添加成员函数HWND HWND()const代码>到您的Wnd。然后,代码将看起来可读,如下所示:
Setparent( p ? p->hwnd() : NULL );
我相信当这些修改完成后,你会发现你的Wnd有什么问题
问题的出现是因为:in?:两侧的操作数必须是相同的类型,因此NULL(0)可以转换为Wnd。因此,*p的临时副本作为?:的返回值,然后调用运算符HWND。必须将?:
运算符的操作数转换为公共类型,并在结果中使用。当你使用
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));
SetParent((HWND) (p != NULL ? Wnd(*p) : Wnd(NULL)));
也就是说,当p
为null时,编译器构造一个临时对象Wnd(null)
(使用您提供的转换构造函数),并将其作为结果返回。此外,编译器也很可能在真分支中构造一个临时对象(通过使用复制构造函数)。然后将生成的临时对象转换为HWND
类型(因为这是SetParent
所需要的),因此整个过程被解释为
SetParent(p != NULL ? *p : Wnd(NULL));
SetParent((HWND) (p != NULL ? Wnd(*p) : Wnd(NULL)));
调用SetParent
后,临时对象立即被销毁。这是您观察到的破坏,只是您错误地将其解释为p
的破坏
编译器选择这种方法的原因是,转换构造函数没有声明为显式的。如果您声明转换构造函数explicit
class Wnd {
...
explicit Wnd(HWND wnd=NULL) : m_hwnd(wnd), fake(NULL) {}
...
};
编译器将不再能够使用将NULL
隐式转换为Wnd
。在这种情况下,编译器将别无选择,只能使用HWND
作为通用类型
SetParent(p != NULL ? (HWND) *p : (HWND) NULL);
就像你想的那样
另外,当类中已经有了运算符HWND()const
时,实现相同的非const版本运算符HWND()
没有意义,您可能需要显示完整的Wnd
类定义。@bacchus,注意您的(HWND))
edit是错误的:D虽然改进的间距很好…@sarnold额外的括号来自句子。我错过了。谢谢你的警告;)请提供Wnd
的定义。没有它,这个问题是完全无法回答的。这是个糟糕的问题。一个人应该学会在提问时发布相关代码。+1我认为删除从HWND
到Wnd
的隐式转换应该可以做到这一点,因为这会禁用从NULL
到Wnd
的转换,编译器将被迫向相反方向转换。无论如何,您完全正确地认为删除隐式转换将使代码更具可读性和可维护性。