Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, remark, Вы писали:
R>>Есть вот такой метод: R>>
R>>void X::foo()
R>>{
R>> void* p = this;
R>>}
R>>
А>X — полиморф?
Я думаю, что через 5 минут после публикации вопроса нецелесообразно давать какие-либо подсказки, т.к. это будет нечестно по отношению к тем, кто хочет выдать свой ответ без каких-либо подсказок.
Здравствуйте, R1K0, Вы писали:
RK>Честно скажу, что ответ не знаю. RK>Но размер 4 навевает мысли о теме адреса указателя... RK>Я хоть на правильном пути или как ?
Я пока не буду давать никаких подсказок. Это будет не честно по отношению к тем, кто хочет решить без подсказок.
Если до завтра не будет ответа, то начну давать подсказки.
R>Вопрос для тех, кто хочет проверить и посоревноваться в знании С++, и возможно узнать новую занятную деталь.
Может этот класс унаследован от нескольких, и эта функция второго базового, поэтому ей передается смещенный this, а при приведении к void* получаем указатель на "полный" объект.
А важно вообще, что это дебажный режим вижуалстудии? Может вижуалстудия что-то вспомогательное хранит, и непосредственно к с++ это отношения не имеет? Информации очень мало, и так угадывать — пальцем в небо.
Здравствуйте, Аноним, Вы писали:
R>>Вопрос для тех, кто хочет проверить и посоревноваться в знании С++, и возможно узнать новую занятную деталь.
А>Может этот класс унаследован от нескольких, и эта функция второго базового, поэтому ей передается смещенный this, а при приведении к void* получаем указатель на "полный" объект.
Засчитывается.
Вначале я хотел попросить пример кода. Но т.к. Bell уже привёл, смысла большого нет.
Здравствуйте, remark, Вы писали:
R>Вопрос: откуда взялось смещение this на 4 при конвертации в void*?
Занятный момент то, что статический тип 'this' есть X*. Хотя физически значение 'this' — указатель на другой объект Y. Соотв. компилятор при *любом* использовании 'this' "устраняет" это досадное недоразумение смещением значения на некоторую величину. Значение этой величины не обязательно sizeof(void*), а может быть любым.
Соотв. это проявляется и в таком коде:
Здравствуйте, Bell, Вы писали:
B>Дело в 4.10/2, а пример вот он:
А можно пояснить? И кстати, натурные эксперименты с VC 2005 и gcc 3.4.4 не дали желанного сдвига.
ЧЯДНТ?
#include <iostream>
using namespace std;
struct X { virtual void foo() {} };
struct Z { int x; virtual ~Z() {} }; // пытался сдвинуть базуstruct Y : Z, X {
void foo()
{
char* c = (char*)this; // на всякий случай, чтобы оббежать прямой кастинг Y* -> void*
cout << this << " " << (void*)this << " " << (void*)c << endl;
// 0x22cc50 0x22cc50 0x22cc50 -- адрес объекта Y целиком
}
};
int main()
{
Y y; y.foo();
cout << &y << " " << &(X&)y << endl;
// 0x22cc50 0x22cc58 -- адрес объекта Y и его базы X
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[3]: Вопрос на засыпку
От:
Аноним
Дата:
25.08.09 10:01
Оценка:
К>натурные эксперименты с VC 2005 и gcc 3.4.4 не дали желанного сдвига.
А если вместо c-style cast сделать dynamic_cast<void*>, что получится?
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, remark, Вы писали:
B>Дело в 4.10/2, а пример вот он:
Честно говоря, я немного затрудняюсь с ходу трактовать 4.10/2
4.10/2
An rvalue of type “pointer to cv T ,” where T is an effective object type, can be converted to an rvalue of
type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points
to the start of the storage location where the object of type T resides, as if the object is a most derived
object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null
pointer value of the destination type.
Разве указатель на объект как на MDT и как на подобъект другого объекта может различаться в принципе?
Я это смещение объяснил для себя несколько по-другому: как артефакт реализации виртуальных функций получаем, что в некоторой функции статический тип this есть T*, а реальное значение this получается указатель на какой-то другой объект Y*. Соотв. компилятору в реализации приходится это учитывать и смещать this при каждом использовании.
Следствие №1: это не регламентируется стандартом, т.к. реализация виртуальных функций не регламентируется.
Следствие №2: это проявляется не только при конвертации к void*, а при *любом* использовании. Например при конвертации к uintptr_t.
Следствие №3: вполне можно представить реализацию виртуальных функций, где статический и динамический типы this всегда будут совпадать, соотв. там не будет таких артефактов в принципе. Для этого, например, можно в таблице виртуальных функций вместе с каждым указателем хранить смещение от базового типа (где впервые объявлена виртуальная функция) до реального типа (в котором переопределена виртуальная функция). Тогда в месте вызова указатель будет соотв. смещаться. Хотя практически это наверное менее эффективно, чем делать это смещение уже внутри реальной функции.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Bell, Вы писали:
B>>Дело в 4.10/2, а пример вот он:
К>А можно пояснить? И кстати, натурные эксперименты с VC 2005 и gcc 3.4.4 не дали желанного сдвига. К>struct Y : Z, X { К> void foo() К> { К> char* c = (char*)this; // на всякий случай, чтобы оббежать прямой кастинг Y* -> void* К> cout << this << " " << (void*)this << " " << (void*)c << endl; К> // 0x22cc50 0x22cc50 0x22cc50 -- адрес объекта Y целиком К> } К>}; К>[/ccode]
Всё правильно. Компилятор смещает this при *любом* использовании. Т.к. расхождение статического и динамического типа this — есть не более чем артефакт реализации виртуальных функций.
См мой комментарий ниже: http://www.rsdn.ru/forum/cpp/3514848.1.aspx
На стеке ещё валяется this от базы (сдвинутый), а Студия срезает угол: вместо того, чтобы сразу восстановить до текущего типа, она предпочитает восстанавливать в местах использования.
Окей, тогда вот такой код
struct X { virtual void* f()=0; };
struct Y { virtual void* f()=0; virtual void* g()=0; };
struct Z : X, Y
{
virtual void* f() { return this; } // манёвры совершаются в функции-переходнике [thunk]:Z::f`adjustor{4}virtual void* g() { return this; } // совершаем манёвры прямо в теле функции
};
int main()
{
Z z;
Y* y = &z;
return y->f() == y->g();
}
Здравствуйте, Кодт, Вы писали:
К>На стеке ещё валяется this от базы (сдвинутый), а Студия срезает угол: вместо того, чтобы сразу восстановить до текущего типа, она предпочитает восстанавливать в местах использования.
Я думаю, что это связано исключительно с дебаг генерацией. В релиз логично смещать в начале функции. Хотя возможно по-месту всё равно будет эффективнее, если например мы имели mov eax, [ecx+20h], заменяем его просто на mov eax, [ecx+40h]. И никаких накладных расходов вообще.
К>Окей, тогда вот такой код
Занятный пример. Честно говоря, я не помню, что бы когда-либо сталкивался с adjustor служебной функцией.
Наверное adjustor всё равно эффективнее смещения this в месте вызова.
Здравствуйте, remark, Вы писали:
R>>Вопрос: откуда взялось смещение this на 4 при конвертации в void*?
R>Занятный момент то, что статический тип 'this' есть X*. Хотя физически значение 'this' — указатель на другой объект Y. Соотв. компилятор при *любом* использовании 'this' "устраняет" это досадное недоразумение смещением значения на некоторую величину. Значение этой величины не обязательно sizeof(void*), а может быть любым.
Вообще изначально вопрос возник при разбирательстве, почему в следующем коде equal получается равным true. Это было бы логично, если бы статические типы this и other различались, ну там приведение между базовым и производным типами. А тут получается, что статические типы одинаковые, однако 2 физических не равных указателя при сравнении дают true.
void T::f()
{
T* other = ...;
// assume this = 0x1000, and other = 0x2000bool equal = (this == other);
...
}