2010-09-30 2 views
5

Статический инструмент проверка показывает нарушение на код ниже:О понижающее приведение из базового класса подкласс указатель

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. Скотт Мейерс "Эффективное использование C++: 50 Конкретные способы улучшить свои программы и дизайн", второе издание, Addison-Wesley, (C) 2005 Pearson Education, Inc., глава : "Наследование и объектно-ориентированное проектирование", пункт 39
  2. Joint Strike Fighter, АВТОМОБИЛЬ AIR, C++ Coding Standards Глава 4.23 преобразование типов, А. В. Правило 178

Как вы думаете, это хорошая практика для того, чтобы не отбрасывать указатель базового класса на указатель подкласса? Почему и когда я должен следовать этому правилу?

+1

Запуск статического анализатора на код MFC? Ты шутишь, да? MFC сама нарушает многие стандарты кодирования JSF, код, генерируемый мастером, больше ломается, он действительно не предназначен для безопасности типов. –

+0

BTW: было бы неплохо прекратить вызов подклассов производных классов. Деривация/наследование - синтаксическая концепция, подклассификация - это типизированная концепция, и эти два являются более или менее независимыми идеями. – Yttrill

ответ

4

Давайте пройти через некоторые понижающее приведение, например, в 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, кнопки и т.д.

Если мы хотим избежать понижающее приведение, вероятно, нам нужно 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

Наконец, спасибо за различный полезный ответ от всех вас.
Они действительно очень полезны.

10

reinterpret_cast, безусловно, плохая идея здесь, независимо от стандартов кодирования или теории ООП. Это должно быть dynamic_cast или boost::polymorphic_downcast.

Что касается главы 39 Эффективного использования C++, он концентрируется на проблемах технического обслуживания, вызванных наличие в обратном приведение к нескольким различным типам и необходимости проверять возвращаемые значения dynamic_cast для потенциальных неудач, в результате нескольких ветвей в коде:

Важно то, что стиль программирования if-then-else, который неизменно приводит к снижению, значительно уступает использованию виртуальных функций, и вы должны зарезервировать его для ситуаций, в которых у вас действительно нет альтернативы.

+4

Или 'static_cast', если известен динамический тип объекта. –

+3

Собственно, то, что я сказал, вводит в заблуждение. 'static_cast (p)' можно использовать безопасно, если '* p' известен как' T'. Для этого 'T' необязательно должен быть динамическим типом' * p'. 'T' также может быть базовым классом динамического типа' * p' и производного класса статического типа '* p'. 'T' необязательно должен быть динамическим типом' * p'. –

0

Это не выглядит так, как будто вам действительно необходимо выполнить бросок или, по крайней мере, не так, как вы это делаете. Ваша функция должна вернуть CWnd, поэтому вам не нужно бросать до CSplitFrame.

Я бы ожидал, что GetChildWnd вернет CWnd* или аналогичный; Почему вы не можете написать что-то вроде: указатели класса

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

m_pMainWnd является членом CWnd, который не имеет доступа к m_OutputBar, который является членом CSplitFrame. – wengseng

0

понижающее приведение в общем-то, что следует избегать любой ценой, в правильно спроектированном коде ООП. Тем не менее, иногда такие приведения необходимы, особенно если вы используете какую-то другую библиотеку/код, который был спроектирован так, чтобы требовать таких отбрасываний в коде клиента. MFC является одним из примеров такой библиотеки.

Когда наводнения действительно необходимы, их никогда не следует выполнять с помощью reinterpret_cast.Правильный способ выполнения литья - либо dynamic_cast, либо static_cast.

Довольно часто люди используют reinterpret_cast, когда они видят приведение в стиле C, используемое для преобразования в код, и принимают решение конвертировать его в стиле C++, неправильно полагая, что приведение в стиле C в таком контексте эквивалентно reinterpret_cast. На самом деле, приведение стиля C, применяемое к паре типа родитель-ребенок, в любом направлении эквивалентно static_cast с некоторыми дополнительными привилегиями (литье в стиле C может прорваться через защиту доступа). Итак, опять же, использование C-стиля для этой цели неверно, но правильная замена C++ не является reinterpret_cast, а скорее dynamic_cast или static_cast.

Смежные вопросы