关于从基类到子类指针的向下转换

时间:2010-09-30 15:56:36

标签: c++ mfc casting static-analysis effective-c++

静态检查工具会在以下代码中显示违规行为:

class CSplitFrame : public CFrameWnd  
...
class CVsApp : public CWinApp
CWnd* CVsApp::GetSheetView(LPCSTR WindowText)
{
 CWnd* pWnd = reinterpret_cast<CSplitFrame*>(m_pMainWnd)->m_OutputBar.GetChildWnd(WindowText);
 return pWnd;
}

错误消息:类'CSplitFrame'继承自'CWnd'类

描述:避免强制转换继承层次结构。 此规则检测从基类指针到子类指针的强制转换。

好处:允许强制转换继承层次结构会导致维护问题,而从基类进行向下转换始终是非法的。

  

参考文献:

     
      
  1. Scott Meyers,“有效的C ++:50种改进程序和设计的具体方法”,第二版,Addison-Wesley,(C)2005 Pearson Education,Inc。,章节:“继承和面向对象设计”,项目39
  2.   
  3. JOINT STRIKE FIGHTER,AIR VEHICLE,C ++编码标准第4.23章类型转换,AV规则178
  4.   

您是否认为不从基类指针转换为子类指针是一个好习惯?为什么以及何时应遵循此规则?

4 个答案:

答案 0 :(得分:10)

无论编码标准或OOP理论如何,

reinterpret_cast在这里肯定是个坏主意。必须是dynamic_castboost::polymorphic_downcast

对于Effective C ++的第39章,它集中讨论由于必须向下转换为多个不同类型而导致的维护问题,并且必须检查dynamic_cast的潜在故障的返回值,从而导致多个分支在代码:

  

重要的是:if-then-else的编程方式,即向下转换总是导致它远远低于虚拟函数的使用,你应该在你真正无法替代的情况下保留它。

答案 1 :(得分:4)

让我们看一下MFC中的一些向下转换示例:

来自CWnd *的CButton *

CWnd* wnd = GetDlgItem(IDC_BUTTON_ID);
CButton* btn = dynamic_cast<CButton*>(wnd);

来自CFrameWnd的CChildWnd *

CChildWnd * pChild = ((CSplitFrame*)(AfxGetApp()->m_pMainWnd))->GetActive();

确实存在MFC设计的一些限制。

由于CWnd提供了MFC中所有窗口类的基本功能,它甚至可以作为View,Dialog,Button等的基类。

如果我们想避免向下转发,可能我们需要MFC黑客将CWnd分成更少的部分?

现在,谈到另一个问题,如何解决违规行为,我的拙见是尽量避免不安全的向下转型,使用安全的转发:

Parent *pParent = new Parent;
Parent *pChild = new Child;

Child *p1 = static_cast<Child*>(pParent);   // Unsafe downcasting:it assigns the address of a base-class object (Parent) to a derived class (Child) pointer
Parent *p2 = static_cast<Child*>(pChild);   // Safe downcasting:it assigns the address of a derived-class object to a base-class pointer

这是使用安全向下转换的良好做法,即使违规仍然存在,我们也会在给出解释的情况下压制违规行为。

很少有用的参考:
http://support.microsoft.com/kb/108587
http://blog.csdn.net/ecai/archive/2004/06/26/27458.aspx
http://www.codeproject.com/KB/mcpp/castingbasics.aspx
http://www.bogotobogo.com/cplusplus/upcasting_downcasting.html

最后,感谢大家的各种有益回应 他们确实非常有帮助。

答案 2 :(得分:0)

对我来说,不管你是否真的有必要进行演员表演,或者至少不像你那样做。您的函数需要返回CWnd,因此您无需转换为CSplitFrame

我原以为GetChildWnd会返回CWnd*或类似内容;你为什么不写下这样的东西:

CWnd* CVsApp::GetSheetView(LPCSTR WindowText)
{
   return m_pMainWnd->m_OutputBar.GetChildWnd(WindowText);
}

答案 3 :(得分:0)

向下转换类指针通常应该在正确设计的OOP代码中不惜一切代价避免。尽管如此,有时候这样的强制转换是必要的,特别是如果你使用的是其他一些设计用于在客户端代码中需要这些强制转换的库/代码。 MFC就是这种库的一个例子。

当真正需要向下转换时,永远不应该使用reinterpret_cast执行转发。执行演员的正确方法是dynamic_caststatic_cast

当人们在代码中看到用于向下转换的C风格转换并且决定将其转换为C ++风格的转换时,人们常常使用reinterpret_cast,错误地认为在这样的上下文中的C风格转换是等效的到reinterpret_cast。实际上,在任何方向上应用于父子类型对的C风格强制转换等同于static_cast并具有一些额外的额外津贴(C风格的强制转换可以突破访问保护)。因此,再次使用C风格的强制转换是错误的,但正确的C ++替换不是reinterpret_cast,而是dynamic_caststatic_cast