Хотелось бы узнать, почему не совпадает мнения у Visual C++ 7 с остальными компиляторами?
А именно, для MS Visual C++ 7 m_c->n::A::Number(); не эквивалентно ((n::A*)m_c)->Number(); в приведённом ниже примере. В результате выполнения строки кода m_c->n::A::Number(); вместо вызова виртуального метода родителя происходит вызов неизвестно чего. Зайдя в дебагере во внутрь этого вызова, видим что указател this смещён на 4 байта.
Избежать проблему можно несколькими способами, но хочу понять именно эту ситуацию.
Варианты правок, чтобы код работал правильно:
— Отказаться от namespace
— В классе I сделать функцию foo() невиртуальной
— При объявлении класса C поменять порядок родительских классов (класс I поставить последним)
— Заменить вызов m_c->n::A::Number(); на ((n::A*)m_c)->Number();
А чтобы вообще получить access violation нужно в исходном примере добавить в класс I член класса (например int i;).
Уважаемые, объясните пожалуйста, как Visual C++ понимает такой вызов? Что он пытается сделать?
#include <iostream>
using namespace std;
namespace n
{
class A
{
public:
void Number() const
{
A_Number();
};
private:
virtual void A_Number() const = 0;
};
class B
{
public:
void Number() const
{
B_Number();
};
private:
virtual void B_Number() const = 0;
};
}
class I
{
public:
virtual void foo()
{
}
};
class C : public I, public n::A, public n::B
{
private:
virtual void A_Number() const
{
cout << "C::A_Number";
};
virtual void B_Number() const
{
cout << "C::B_Number";
};
};
class D: public n::A, public n::B
{
public:
D()
{
m_c = new C;
};
~D()
{
delete m_c;
m_c = NULL;
};
private:
C* m_c;
virtual void A_Number() const
{
cout << "D::A_Number -> ";
m_c->n::A::Number(); // only ((n::A*)m_c)->Number(); work ok!
cout << endl;
}
virtual void B_Number() const
{
cout << "D::B_Number -> ";
m_c->n::B::Number(); // only ((n::B*)m_c)->Number(); work ok!
cout << endl;
}
};
int main()
{
D d;
d.n::A::Number();
d.n::B::Number();
return 0;
}
Здравствуйте, McKolby, Вы писали:
MK>Хотелось бы узнать, почему не совпадает мнения у Visual C++ 7 с остальными компиляторами? MK>А именно, для MS Visual C++ 7 m_c->n::A::Number(); не эквивалентно ((n::A*)m_c)->Number(); в приведённом ниже примере. В результате выполнения строки кода m_c->n::A::Number(); вместо вызова виртуального метода родителя происходит вызов неизвестно чего. Зайдя в дебагере во внутрь этого вызова, видим что указател this смещён на 4 байта.
Попробовал на VC++ 7.1. Проблема заключается в том, что при вызове m_c->n::A::Number(); this не сдвигается на 4 байта (сдвиг должен происходить из-за того, что класс I в списке наследования стоит до A). В результате используется vtbl сласса I, последсвия чего могут быть самыми разнообразными.
При явном касте ((n::A*)m_c)->Number(); производится сдвиг this , и вызывается правильный метод.
(Кстати тут лучше явно использовать static_cast. В данном примерер, конечно, (n::A*)m_c разворачивается именно в static_cast<n::A*>, но береженого Бог бережет...)
MK>Избежать проблему можно несколькими способами.
...
ИМХО лучше отказаться от избыточной квалификации:
ЗЫ
Мне лень копаться в стандарте и искать, является ли такая "расширенная" квалификация допустимой Однако ИМХО такой синтаксис вполне допустим, и мы имеем дело с багом компилятора.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>McKolby,
>> Хотелось бы узнать, почему не совпадает мнения у Visual C++ 7 с остальными компиляторами?
ПК>Ошибка.
Pochemu bug ne bil opublikovan samim avtorom? Poluchaetsya svoego roda narushenie avtorskogo prava.
McKolby,
> Отдельное спасибо Павлу Кузнецову за столь подробный и развёрнутый ответ.
Искренне прошу прощения. Я не подумал, что для Вас могло быть важным сообщить об ошибке лично. В качестве утешения могу сказать, что ошибку разработчики признали.
Posted via RSDN NNTP Server 2.0
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен