((SA*)0)->Func();
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 26.04.02 19:52
Оценка:
Должен ли по стандарту работать следующий код?
struct SA
{
  void Func() //невиртуальная
  {
    if (this == NULL) 
      return;
  }  
  static void StaticFunc() {}
};

void main()
{
  ((SA*)0)->StaticFunc(); //ИМХО, тут должно быть все нормально
  ((SA*)0)->Func(); //а вот здесь не понятно, мало ли чего может компилятор напихать
}
Re: ((SA*)0)->Func();
От: IT Россия linq2db.com
Дата: 26.04.02 20:02
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Должен ли по стандарту работать следующий код?


Давай перепишем и посмотрим причём тут стандарт:

struct SA
{
  void Func(); //невиртуальная и не важно
  static void StaticFunc();
};

void f(SA*)
{
  SA->Func(); // откуда компилятору знать, что у программера на уме
}

void main()
{
  SA::StaticFunc(); // можно обойтись и без объекта
  f((SA*)0);        // мало ли чего можно компилятору подсунуть
}
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: ((SA*)0)->Func();
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 26.04.02 20:17
Оценка:
Здравствуйте IT, Вы писали:

IT>Здравствуйте DarkGray, Вы писали:


DG>>Должен ли по стандарту работать следующий код?


IT>Давай перепишем и посмотрим причём тут стандарт:


IT>
IT>struct SA
IT>{
IT>  void Func(); //невиртуальная и не важно

//как раз важно что функция не виртуальная, для виртуальной функции все будет падать со свистом при нулевом указателе

IT>  static void StaticFunc();
IT>};

IT>void f(SA*)
IT>{
IT>  SA->Func(); // откуда компилятору знать, что у программера на уме
IT>}

IT>void main()
IT>{
IT>  SA::StaticFunc(); // можно обойтись и без объекта
IT>  f((SA*)0);        // мало ли чего можно компилятору подсунуть
IT>}
IT>


Наверное, не правильно вопрос задал...

Написано ли где-нибудь в стандарте, что компилятор не должен закладываться на то, что this всегда не равен нулю?

Для виртуальный функций — понятно. Компилятор не явно закладывается на то, что this не нуль и этот указатель использует в своих целях, по нему ищет таблицу виртуальных функции.
В случае нулевого указателя все падает.

Для статик функции то же — понятно, так как this-а у них совсем нет.

А вот для обычных функций не понятно. Могу ли я в качестве this при вызове использовать произвольное значение (в частности NULL) или не могу? То есть будет это приводить к падению или не будет?
Re: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 26.04.02 20:19
Оценка: 6 (1)
Здравствуйте DarkGray, Вы писали:

DG>Должен ли по стандарту работать следующий код?

DG>
DG>struct SA
DG>{
DG>  void Func() //невиртуальная
DG>  {
DG>    if (this == NULL) 
DG>      return;
DG>  }  
DG>  static void StaticFunc() {}
DG>};

DG>void main()
DG>{
DG>  ((SA*)0)->StaticFunc(); //ИМХО, тут должно быть все нормально
DG>  ((SA*)0)->Func(); //а вот здесь не понятно, мало ли чего может компилятор напихать
DG>}
DG>


Нет, не должен. По стандарту, применение оператора '->' к null-указателю, вызывает undefined behavior.
Best regards,
Андрей Тарасевич
Re[3]: ((SA*)0)->Func();
От: IT Россия linq2db.com
Дата: 26.04.02 20:26
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>А вот для обычных функций не понятно. Могу ли я в качестве this при вызове использовать произвольное значение (в частности NULL) или не могу? То есть будет это приводить к падению или не будет?


Может быть, если твой метод имеет доступ к членам класса или вызывает другие методы, которые это делают. И точно будет, если твой метод вызывает виртуальные функции.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 26.04.02 20:32
Оценка: 34 (5)
Здравствуйте DarkGray, Вы писали:

DG>Написано ли где-нибудь в стандарте, что компилятор не должен закладываться на то, что this всегда не равен нулю?


Может написано, может не написано. Это, в принципе, неважно. Стандарт вместо этого совершенно глухо запрещает все возможные пути падания внутрь метода объекта через null-указатель. А именно: запрещается разадресовывать null-указатель. Это автоматически означает, что, пока поведение программы определено, внутри метода объекта this никак не может быть равен null-указателю.

DG>А вот для обычных функций не понятно. Могу ли я в качестве this при вызове использовать произвольное значение (в частности NULL) или не могу? То есть будет это приводить к падению или не будет?


Будет приводить к неопределенному поведению. А будет падать или нет — это уже дело десятое. Твоя попытка защититься от такой ситуации путем сравнения 'this' с 'NULL' в рамках языка C++ ситуацию никак не спасает, т.к. у этому моменту ее уже поздно спасать.

Даже если некоторый компилятор все таки делает "корректный" вызов метода через null-указатель, все равно проверка 'this' на NULL в общем случае работать не будет. Т.к., например, нигде не гарантируется, что указатель 'this' базового класса совпадает с указателем 'this' класса-наследника. Даже при одиночном наследовании. А при множественном наследовании твоя проверка в принципе не способна отловить NULL. Посмотри что получится если сделать так:

struct SX { int x; };

struct SB : SX, SA {};

((SB*) 0)->Func();
Best regards,
Андрей Тарасевич
Re[3]: ((SA*)0)->Func();
От: Alex Fedotov США  
Дата: 26.04.02 20:36
Оценка: 3 (1)
Здравствуйте DarkGray, Вы писали:

DG>А вот для обычных функций не понятно. Могу ли я в качестве this при вызове использовать произвольное значение (в частности NULL) или не могу? То есть будет это приводить к падению или не будет?


Как Андрей уже сказал, вызов метода с недействительным значением this приводит к undefined behavior. Это, однако, не останавливает разработчиков некоторых библиотек он написания вот такого кода:

_AFXWIN_INLINE HWND CWnd::GetSafeHwnd() const
    { return this == NULL ? NULL : m_hWnd; }
-- Alex Fedotov
Re[4]: ((SA*)0)->Func();
От: Silver_s Ниоткуда  
Дата: 27.04.02 05:52
Оценка:
Здравствуйте Alex Fedotov, Вы писали:

AF>Как Андрей уже сказал, вызов метода с недействительным значением this приводит к undefined behavior. Это, однако, не останавливает разработчиков некоторых библиотек он написания вот такого кода:


AF>
AF>_AFXWIN_INLINE HWND CWnd::GetSafeHwnd() const
AF>    { return this == NULL ? NULL : m_hWnd; }
AF>


А как бы им надо было написать чтобы реализовать такую функциональность и соответствовать стандарту(ну или не использовать плохой стиль программирования)
Например объявить функцию за пределами класса:

inline HWND GetSafeHwnd(CWnd *pW)
{
   return pW == NULL ? NULL : pW->m_hWnd; 
}

Здесь вроде придратся не к чему (даже плохим стилем не обзовешь), но это абсолютно то же самое что и в первом случае.
А в случае множественного наследования проблема с null указателями есть не только у функций членов. Просто не верно утверждение "если производный класс null то и базовые классы null" и это прийдется учитывать всегда.
И что плохого в том, что в не virtual функции используется this==null, в конце концов после компиляции в obj файле она будет лежать как обычная функция, у которой первый параметр сравнивается с 0.
Re[5]: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 27.04.02 06:00
Оценка: 3 (1)
Здравствуйте Silver_s, Вы писали:

SS>Например объявить функцию за пределами класса:


SS>
SS>inline HWND GetSafeHwnd(CWnd *pW)
SS>{
SS>   return pW == NULL ? NULL : pW->m_hWnd; 
SS>}
SS>

SS>Здесь вроде придратся не к чему (даже плохим стилем не обзовешь), но это абсолютно то же самое что и в первом случае.

Нет, это не абсолютно то же самое. Теперь, чтобы вызвать эту функцию для указателя на класс, унаследованный от 'CWnd', компилятору придется выполнить конверсию типа указателя. При выполнении конверсии гарантируется, что null-указатель одного типа будет сконвертирован именно в null-указатель другого типа. Независимо от типа наследования — одиночное или множественное. Поэтому такой вариант с внешней функцией гарантированно будет работать всегда. А работоспособность исходного варианта с функцией-членом ничем не гарантирована. В случае множественного наследования исходный вариант просто сядет в лужу.

SS>А в случае множественного наследования проблема с null указателями есть не только у функций членов. Просто не верно утверждение "если производный класс null то и базовые классы null" и это прийдется учитывать всегда.


Если проверка на NULL делается во внешней функции, то никаких проблем с множественным наследованием нет. Даже задумываться об этом не надо.
Best regards,
Андрей Тарасевич
Re[5]: ((SA*)0)->Func();
От: Кодт Россия  
Дата: 27.04.02 06:05
Оценка:
Здравствуйте Silver_s, Вы писали:

SS>А в случае множественного наследования проблема с null указателями есть не только у функций членов. Просто не верно утверждение "если производный класс null то и базовые классы null" и это прийдется учитывать всегда.

SS>И что плохого в том, что в не virtual функции используется this==null, в конце концов после компиляции в obj файле она будет лежать как обычная функция, у которой первый параметр сравнивается с 0.

Короче, по стандарту не написано, поэтому все компиляторы наплюют на нуллевой this (а могли бы, могли бы! Например, считать, что NULL-потомок имеет всех NULL-предков).

Непонятно, правда — как реагировать тогда, когда нужно разыменовать NULL.

(BYTE*)(NULL)[0] = 1; // исключение защиты памяти
(CObject)(NULL)->VirtualFn(); // в принципе, то же, но — не факт, не факт...

Да и код утяжелится проверками.
Перекуём баги на фичи!
Re[4]: ((SA*)0)->Func();
От: Alexander Shargin Россия RSDN.ru
Дата: 27.04.02 17:37
Оценка:
Здравствуйте Alex Fedotov, Вы писали:

AF>Как Андрей уже сказал, вызов метода с недействительным значением this приводит к undefined behavior. Это, однако, не останавливает разработчиков некоторых библиотек он написания вот такого кода:


AF>
AF>_AFXWIN_INLINE HWND CWnd::GetSafeHwnd() const
AF>    { return this == NULL ? NULL : m_hWnd; }
AF>


От себя добавлю, что при реализации стандартной библиотеки также иногда прибегают к разименованию нулевого указателя. Например, так реализован макрос offsetof, который интенсивно используется как в MFC, так и в ATL:

#define offsetof(s,m)   (size_t)&(((s *)0)->m)
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[5]: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 27.04.02 17:51
Оценка: 5 (1)
Здравствуйте Alexander Shargin, Вы писали:

AF>>Как Андрей уже сказал, вызов метода с недействительным значением this приводит к undefined behavior. Это, однако, не останавливает разработчиков некоторых библиотек он написания вот такого кода:


AF>>
AF>>_AFXWIN_INLINE HWND CWnd::GetSafeHwnd() const
AF>>    { return this == NULL ? NULL : m_hWnd; }
AF>>


AS>От себя добавлю, что при реализации стандартной библиотеки также иногда прибегают к разименованию нулевого указателя. Например, так реализован макрос offsetof, который интенсивно используется как в MFC, так и в ATL:


AS>
AS>#define offsetof(s,m)   (size_t)&(((s *)0)->m)
AS>


Реализация стандартной библиотеки имеет право опираться на любые implementation defined особенности платформы, в том числе на определенность "неопределенного поведения". При этом, разумеется, требуется, чтобы поведение оставалось определенным для всех легальных применений данной конструкции. Для вышеприведенной реализации 'offsetof' это условие выполняется. Для 'GetSafeHwnd' — нет.

Для того, чтобы "легализовать" такую реализацию 'GetSafeHwnd' MFC должно либо явно запрещать использование класса 'CWnd' в качестве базового класса в иерархии с множественным наследованием, либо запрещать вызов 'GetSafeHwnd' для экземпляров класса, имеющих множественных предков, либо еще как-то ограничить использование 'GetSafeHwnd'. Вполне может быть, что такие ограничения существуют. Я, правда, о них пока не слышал.
Best regards,
Андрей Тарасевич
Re[6]: ((SA*)0)->Func();
От: Alex Fedotov США  
Дата: 27.04.02 19:29
Оценка: +1
Здравствуйте Андрей Тарасевич, Вы писали:

АТ>Для того, чтобы "легализовать" такую реализацию 'GetSafeHwnd' MFC должно либо явно запрещать использование класса 'CWnd' в качестве базового класса в иерархии с множественным наследованием, либо запрещать вызов 'GetSafeHwnd' для экземпляров класса, имеющих множественных предков, либо еще как-то ограничить использование 'GetSafeHwnd'. Вполне может быть, что такие ограничения существуют. Я, правда, о них пока не слышал.


Таки есть ограничения:
TN016: Using C++ Multiple Inheritance with MFC
-- Alex Fedotov
Re[7]: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 27.04.02 19:34
Оценка:
Здравствуйте Alex Fedotov, Вы писали:

AF>Здравствуйте Андрей Тарасевич, Вы писали:


АТ>>Для того, чтобы "легализовать" такую реализацию 'GetSafeHwnd' MFC должно либо явно запрещать использование класса 'CWnd' в качестве базового класса в иерархии с множественным наследованием, либо запрещать вызов 'GetSafeHwnd' для экземпляров класса, имеющих множественных предков, либо еще как-то ограничить использование 'GetSafeHwnd'. Вполне может быть, что такие ограничения существуют. Я, правда, о них пока не слышал.


AF>Таки есть ограничения:

AF>TN016: Using C++ Multiple Inheritance with MFC

Это, в некотором смысле, оправдывает вышеприведенный вариант 'GetSafeHwnd', как деталь реализации платформеннозависимой библиотеки.
Best regards,
Андрей Тарасевич
Re[8]: ((SA*)0)->Func();
От: Alexander Shargin Россия RSDN.ru
Дата: 27.04.02 19:50
Оценка: 15 (1)
Здравствуйте Андрей Тарасевич, Вы писали:

АТ>Здравствуйте Alex Fedotov, Вы писали:


AF>>Здравствуйте Андрей Тарасевич, Вы писали:


АТ>>>Для того, чтобы "легализовать" такую реализацию 'GetSafeHwnd' MFC должно либо явно запрещать использование класса 'CWnd' в качестве базового класса в иерархии с множественным наследованием, либо запрещать вызов 'GetSafeHwnd' для экземпляров класса, имеющих множественных предков, либо еще как-то ограничить использование 'GetSafeHwnd'. Вполне может быть, что такие ограничения существуют. Я, правда, о них пока не слышал.


AF>>Таки есть ограничения:

AF>>TN016: Using C++ Multiple Inheritance with MFC

АТ>Это, в некотором смысле, оправдывает вышеприведенный вариант 'GetSafeHwnd', как деталь реализации платформеннозависимой библиотеки.


MFC платформеннонезависимой никогда не планировалась. Она использует не только MS-specific код, но и ассемблерные вставки. Что касается MI, то никто не мешает мне "подмешать" свои классы к классам MFC — с точки зрения MFC это абсолютно законно. Но даже если брать два класса, порождённых от CObject, MI возможно, как показывает пример из той же приведённой Алексом статьи:

#include <afxwin.h>

class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{ 
public:
    CHelloAppAndFrame()
        { }

    // Necessary evil for MI disambiguity
    void* operator new(size_t nSize)
        { return CFrameWnd::operator new(nSize); }
    void operator delete(void* p)
        { CFrameWnd::operator delete(p); }

// Implementation
    // CWinApp overrides
    virtual BOOL InitInstance();
    // CFrameWnd overrides
    virtual void PostNcDestroy();
    afx_msg void OnPaint();

    DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP(CHelloAppAndFrame, CFrameWnd)
    ON_WM_PAINT()
END_MESSAGE_MAP()

// since the frame window is not allocated on the heap, we must
// override PostNCDestroy not to delete the frame object
void CHelloAppAndFrame::PostNcDestroy()
{
    // do nothing (do not call base class)
}

void CHelloAppAndFrame::OnPaint()
{
    CPaintDC dc(this);
    CRect rect;
    GetClientRect(rect);

    CString s = "Hello, Windows!";
    dc.SetTextAlign(TA_BASELINE | TA_CENTER);
    dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
    dc.SetBkMode(TRANSPARENT);
    dc.TextOut(rect.right / 2, rect.bottom / 2, s);
}

// Application initialization
BOOL CHelloAppAndFrame::InitInstance()
{
    // first create the main frame
    if (!CFrameWnd::Create(NULL, "Multiple Inheritance Sample",
        WS_OVERLAPPEDWINDOW, rectDefault))
        return FALSE;

    // the application object is also a frame window
    m_pMainWnd = this;          
    ShowWindow(m_nCmdShow);
    return TRUE;
}

CHelloAppAndFrame theHelloAppAndFrame;
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[9]: ((SA*)0)->Func();
От: Андрей Тарасевич Беларусь  
Дата: 28.04.02 04:56
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

AF>>>Таки есть ограничения:

AF>>>TN016: Using C++ Multiple Inheritance with MFC

АТ>>Это, в некотором смысле, оправдывает вышеприведенный вариант 'GetSafeHwnd', как деталь реализации платформеннозависимой библиотеки.


AS>MFC платформеннонезависимой никогда не планировалась. Она использует не только MS-specific код, но и ассемблерные вставки.


Я так и написал: "...как деталь реализации платформеннозависимой библиотеки..."

AS>Что касается MI, то никто не мешает мне "подмешать" свои классы к классам MFC — с точки зрения MFC это абсолютно законно.


Да, законно. При выполненияи того условия, что 'CWnd' будет первой базой любого унаследованного класса, как сказано в статье.
Best regards,
Андрей Тарасевич
Re[3]: ((SA*)0)->Func();
От: VVV Россия  
Дата: 28.04.02 10:47
Оценка: 6 (1)
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте IT, Вы писали:


IT>>Здравствуйте DarkGray, Вы писали:


DG>>>Должен ли по стандарту работать следующий код?


IT>>Давай перепишем и посмотрим причём тут стандарт:


IT>>
IT>>struct SA
IT>>{
IT>>  void Func(); //невиртуальная и не важно

DG>//как раз важно что функция не виртуальная, для виртуальной функции все будет падать со свистом при нулевом указателе

IT>>  static void StaticFunc();
IT>>};

IT>>void f(SA*)
IT>>{
IT>>  SA->Func(); // откуда компилятору знать, что у программера на уме
IT>>}

IT>>void main()
IT>>{
IT>>  SA::StaticFunc(); // можно обойтись и без объекта
IT>>  f((SA*)0);        // мало ли чего можно компилятору подсунуть
IT>>}
IT>>


DG>Наверное, не правильно вопрос задал...


DG>Написано ли где-нибудь в стандарте, что компилятор не должен закладываться на то, что this всегда не равен нулю?


DG>Для виртуальный функций — понятно. Компилятор не явно закладывается на то, что this не нуль и этот указатель использует в своих целях, по нему ищет таблицу виртуальных функции.

DG>В случае нулевого указателя все падает.

DG>Для статик функции то же — понятно, так как this-а у них совсем нет.


DG>А вот для обычных функций не понятно. Могу ли я в качестве this при вызове использовать произвольное значение (в частности NULL) или не могу? То есть будет это приводить к падению или не будет?


Если посмотреть с другой стороны, то любой вызов C++ функции можно описать в терминам C функции, т.е. это будет функция, где первым параметром передаётся указатель на какую-то структуру даннык (this). Посему, при вызове такой функции из C этот параметр (this) можно задать каким угодно (и NULL в том числе). Например, если посмотреть МАПИшные примеры, то там все указатели проверяются на валидность, например так:

/* BAD_STANDARD_OBJ
 *
 * This macro insures that the object is a writable object of the correct size
 * and that this method belongs to the object.
 *
 * NOTES ON USE!
 *  This depends upon using the standard method of declaring the object
 *  interface.
 *
 *  prefix is the method prefix you chose when declaring the object interface.
 *  method is the standard method name of the calling method.
 *  lpVtbl is the name of the lpVtbl element of your object.
 */
#define BAD_STANDARD_OBJ( lpObj, prefix, method, lpVtbl)     (   IsBadWritePtr( (lpObj), sizeof(*lpObj))      || IsBadReadPtr( (void *) &(lpObj->lpVtbl->method), sizeof(LPVOID))      ||( ( LPVOID) (lpObj->lpVtbl->method) != (LPVOID) (prefix##method)))

#define FBadUnknown( lpObj )     (   IsBadReadPtr( (lpObj), sizeof(LPVOID) )      || IsBadReadPtr( (lpObj)->lpVtbl, 3 * sizeof(LPUNKNOWN) )      || IsBadCodePtr( (FARPROC)(lpObj)->lpVtbl->QueryInterface ))
this->Func() и Func(this)
От: Андрей Тарасевич Беларусь  
Дата: 28.04.02 17:45
Оценка: 47 (7)
#Имя: FAQ.cpp.functhis
Здравствуйте VVV, Вы писали:

VVV>Если посмотреть с другой стороны, то любой вызов C++ функции можно описать в терминам C функции, т.е. это будет функция, где первым параметром передаётся указатель на какую-то структуру данных (this). Посему, при вызове такой функции из C этот параметр (this) можно задать каким угодно (и NULL в том числе).


В том то и дело, что тут есть одна существенная тонкость, из-за которой вызов метода объекта в С++ нельзя так запросто описать в терминах C функций.

Предположим, что у нас имеется вот такой класс A

struct A 
{ 
  void foo(int i) { cout << (this != NULL ? "not NULL" : "NULL") << " " << i << endl; }
};


Перепишем метод 'A::foo' в виде "эквивалентной" С функции, как ты предлагаешь (если я тебя правильно понял):

void foo(A* this_, int i) { cout << (this_ != NULL ? "not NULL" : "NULL") << " " << i << endl; }


Пусть от класса A и класса B множественно унаследован класс C:

struct B { int x; };

struct C : B, A {};


Рассмотрим вот такой вызов метода 'foo' через null-указатель типа 'C*':

C* p = NULL;

p->foo(5); // (1)


Как будет выглядеть соответствующий вызов С функции 'foo'? Он будет выглядеть вот так:

foo(p, 5); // (2)


Эквивалентны ли эти вызовы (закроем глаза на неопределенное поведение в вызове (1))? Нет, не эквивалентны. При компиляции, например, MSVC++, эти вызовы распечатают разные результаты на экране. Почему так происходит?

Происходит это потому, что в вызове (2) компилятор вынужден сделать стандартную pointer conversion — конверсию типа первого параметра от типа 'C*' к типу 'A*'. При этом, согласно спецификации языка С++, null-указатель одного типа должен быть переведен в null-указатель другого типа. По этой причине в результате вызова (2) внутрь функции 'foo' будет передан null-указатель и неравенство 'this_ != NULL' не выполнится.

А в вызове (1) ситуация совершенно иная. Для того, чтобы взывать метод класса A через указатель типа 'C*', этот указатель тоже необходимо определенным образом "преобразовать". То, что происходит с указателем в таком случае, не является никакой "официальной" конверсией языка С++. Компилятор обязан просто как-то "сдвинуть" указатель типа 'C*' так, чтобы он стал указывать на экземпляр класса A внутри экземпляра класса C. Как компиялтор будет это делать — детали реализации. Никаких требований о том, чтобы null-указатель одного типа при этом первращался именно в null-указатель другого типа, нет и никогда не было. Эти требования не нужны по той простой причине, что стандарт С++ целиком и полностью запрещает такие вызовы. Ни один уважающий себя компилятор не будет тратить время на проверку равенства указателя NULL. Компилятор просто сдвинет указатель на определенное количесво байт. При этом null-указатель перестанет быть null-указателем. По этой самой причине в результате вызова (1) внутри метода 'A::foo' указатель this не будет null-указателем и неравенство 'this_ != NULL' выполнится.

Вот если бы мы сами заставили компилятор выполнить полноценную конверсию указателя при вызове метода

((A*) p)->foo(5); // (3)


тогда результаты выполнения вызовов (2) и (3) совпали бы. Но пользоваться в программах именно такими вызовами только ради того, чтобы сохранить "правильность" преобразования null-указателей, никто, разумеется, не будет. К тому же это потенциальный удар по производительности.

Отсюда мораль: с ситауациях с множественным наследованием (а согласно стандарту языка — в ситуациях с любым наследованием) не надо думать о вызовах методов классов, как о вызовах обычных С функций с дополнительным параметром. Это несколько разные вещи.
Best regards,
Андрей Тарасевич
Re[5]: ((SA*)0)->Func();
От: Silver_s Ниоткуда  
Дата: 29.04.02 05:58
Оценка:
Да, оказывается такое преобразование есть.
class C:A,B{...
При преобразовании от B к C в disassembly виднеются сравнения с 0 и условные переходы.
Re[9]: ((SA*)0)->Func();
От: Sergey Россия  
Дата: 29.04.02 06:05
Оценка: +1
Здравствуйте Alexander Shargin, Вы писали:


AS>MFC платформеннонезависимой никогда не планировалась.


Неправда ваша Если мне окончательно не отшибло память, то та версия MFC, что шла с VC 4.0, существовала не только для Win32, но и для Mac OS.

AS>Она использует не только MS-specific код, но и ассемблерные вставки.

Ну дык реализация библиотеки для конкретной платформы могет делать что ей заблагорассудится. Сишная RTL от MS тоже кое-где на ассемблере писана, главное чтоб интерфейс был одинаковымю
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.