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

发布于 2024-09-25 17:43:57 字数 774 浏览 18 评论 0原文

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

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. 联合攻击战斗机、飞行器、C++ 编码标准第 4.23 章类型转换、AV 规则 178

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

A static check tool shows a violation on the below code:

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;
}

Error Message: Class 'CSplitFrame' inherits from class 'CWnd'

Description: Avoid casts down the inheritance hierarchy.
This rule detects casts from a base class pointer to a subclass pointer.

Benefits: Allowing casts down the inheritance hierarchy leads to maintenance problems, and downcasting from a base class is always illegal.

References:

  1. Scott Meyers, "Effective C++: 50 Specific Ways to Improve Your Programs and Design", Second Edition, Addison-Wesley, (C) 2005 Pearson Education, Inc., Chapter: "Inheritance and Object-Oriented Design", Item 39
  2. JOINT STRIKE FIGHTER, AIR VEHICLE, C++ CODING STANDARDS Chapter 4.23 Type Conversions, AV Rule 178

Do you think it's a good practice for not casting down from a base class pointer to a subclass pointer? Why and When should I follow this rule?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

粉红×色少女 2024-10-02 17:43:57

无论编码标准或 OOP 理论如何,reinterpret_cast 在这里肯定是一个坏主意。它必须是 dynamic_castboost::polymorphic_downcast

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

重要的是:向下转型必然导致的 if-then-else 编程风格远不如使用虚函数,您应该将其保留在您确实别无选择的情况下。

reinterpret_cast is certainly a bad idea here, regardless of coding standards or OOP theory. It has to be dynamic_cast or boost::polymorphic_downcast.

As for the chapter 39 of Effective C++, it concentrates on the maintenance problems caused by having to downcast to multiple different types and having to check the return values of dynamic_cast for potential failures, resulting in multiple branches in the code:

The important thing is this: the if-then-else style of programming that downcasting invariably leads to is vastly inferior to the use of virtual functions, and you should reserve it for situations in which you truly have no alternative.

幻梦 2024-10-02 17:43:57

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

CButton* 来自 CWnd*

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

CChildWnd* 来自 CFrameWnd*

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

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

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

如果我们想避免向下转型,可能我们需要MFC hacker将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

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

Let us go through some of the downcasting example in MFC:

CButton* from CWnd*

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

CChildWnd* from CFrameWnd*

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

There are indeed some of the limitation of MFC design.

Due to CWnd provides the base functionality of all window classes in MFC, it does even serve as a base class of View, Dialog, Button etc.

If we want to avoid downcasting, probably we need MFC hacker to split CWnd into fewer pieces?

Now, comes to the another question, how to solve the violation, my humble opinion is try to avoid unsafe downcasting, by using safe downcasting:

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

It serve as good practise for using safe downcasting, even though the violation is still exists, we will just suppress the violation with given explanation.

Few of the useful reference:
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

Lastly, thanks for various useful response from all of you.
They are indeed very helpful.

猫性小仙女 2024-10-02 17:43:57

在我看来,无论如何,你实际上没有必要执行演员阵容,或者至少没有你正在做的那样。您的函数需要返回 CWnd,因此您无需转换为 CSplitFrame

我本来期望 GetChildWnd 返回一个 CWnd* 或类似的;为什么你不能写这样的东西:

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

It doesn't look to me like it is actually necessary for you to perform the cast anyway, or at least not as you are doing it. Your function needs to return a CWnd, so you don't need to cast up to a CSplitFrame.

I would have expected that GetChildWnd returns a CWnd* or similar; why can you not write something like:

CWnd* CVsApp::GetSheetView(LPCSTR WindowText)
{
   return m_pMainWnd->m_OutputBar.GetChildWnd(WindowText);
}
鸢与 2024-10-02 17:43:57

在正确设计的 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

Downcasting class pointers is generally something that should be avoided at all costs in properly designed OOP code. Nevertheless, sometimes such casts are necessary, especially if you are using some other library/code that was designed to require such casts in the client code. MFC is one example of such library.

When downcasts are really necessary, they should never be performed with reinterpret_cast. The proper way to perform the cast is either dynamic_cast or static_cast.

Quite often people use reinterpret_cast when they see a C-style cast used for downcast in the code and decide to convert it to C++-style cast, incorrectly assuming that a C-style cast in such context is equivalent to reinterpret_cast. In reality, C-style cast applied to parent-child type pair in any direction is equivalent to static_cast with some extra perks (C-style cast can break through access protection). So, again, using C-style casts for this purpose is wrong, but the proper C++ replacement is not reinterpret_cast, but rather dynamic_cast or static_cast.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文