2015-08-08 1 views
3

Мой кодПочему static_cast предоставляет разные ячейки памяти для одного и того же объекта?

class Parent { int a; }; 
class Child { int b; }; 
struct GrandChild : public Parent, public Child { int a, b, c; }; 

int main() { 
    GrandChild GC; 
    std::cout << "GrandChild's address is at : " <<&GC<<endl; 
    std::cout << "Child's address is at : " <<static_cast<Child*>(&GC)<<endl; 
    std::cout << "Parent's address is at : " <<static_cast<Parent*>(&GC)<<endl; 

} 

выход:

GrandChild's address is at : 0077F6F8 
Child's address is at : 0077F6FC 
Parent's address is at : 0077F6F8 

Почему после static_cast ячейки памяти имеют противоречия, как и выше?

+1

Интересно ... узнал что-то новое сегодня, спасибо за удивительный вопрос! – emlai

+1

В стороне, эти имена типов крайне вводят в заблуждение! –

+0

@LightnessRacesinOrbit: sry, я не понял, как :( – InQusitive

ответ

10

GrandChild происходит от обоих Parent и Child. Таким образом, объект GrandChild в памяти состоит из объекта Parent и объекта Child в его памяти.

&GC сам по себе возвращает адрес памяти в GrandChild объекта в целом

static_cast<Parent*>(&GC) возвращает начальный адрес Parent части внутри GrandChild объекта.

static_cast<Child*>(&GC) возвращает начальный адрес части Child внутри объекта GrandChild.

В вашем случае, Grandchild происходит от Parent первого, так что Parent часть выравнивается в начале блока хранятся в памяти GrandChild «ы. Затем участок Child следует за участком Parent. Вот иллюстрация, чтобы показать, что:

structure

+0

Эта фотография показала мне, где я был не прав в комментариях, спасибо. –

5

&GC - адрес объекта GrandChildGC. static_cast<Child*>(&GC) - адрес ChildподобъектGC. И static_cast<Parent*>(&GC) - это адрес Parentподобъект of GC.

В вашей конкретной реализации, представляется, что GrandChild объекта начинается с Parent подобъектом, а затем Child субобъекта приходит после, поэтому адрес Parent подобъекта является таким же, как адрес полноциклового GrandChild объекта, но первый байтом в Child подобъекте является не первый байт полного GrandChild объект, поэтому его адрес больше. Вы, однако, не можете полагаться на это поведение, будучи переносимым; различные реализации могут выделять субобъекты базового класса и члена в разных порядках и даже не должны быть совместимыми между разными классами.

+0

Почему начало записи аналогично для «Parent» и «GrandChild» в этом примере? они должны быть выровнены в порядке возрастания памяти и как они разделяют один и тот же кусок памяти? –

+2

@VictorPolevoy Объект 'GrandChild' содержит в себе как объект« Parent », так и объект« Child ». компилятор, объект «Parent» выделяется в самом начале объекта «GrandChild». Они разделяют кусок памяти, потому что объект «Parent» действительно является частью * объекта «GrandChild». – Brian

+0

Я имею в виду, что, по моему мнению, классы в памяти должны быть выровнены следующим образом: «Base1, Base2, Base 3, ...» с их собственными данными, как описано [здесь] (http://eli.thegreenplace.net/ 2012/12/17/демпинг-Ac-объекты-память макет-с лязгом). Итак, согласно clang, «Parent :: a» должен быть началом текущего макета объекта, а «GrandChild :: a» должен быть началом памяти объекта «GrandChild» без какого-либо пересечения. –

2

В вашем примере, это предсказуемо, даже если она определяется реализацией (но исполнители имеют право выбирать прямые решения для простых случаев :-))

Здесь представление памяти вашего объекта (выводится из адресов , а не от стандарта):

Parent::a (int = 4 bytes) 
Child::b (int = 4 bytes) 
GrandChild::a 
GrandChild::b 
GrandChild::c 

Это из-за вашей декларации: GrandChild наследует первый из Parent, рядом с Child. С этим представлением имеет смысл, что адрес Parent будет таким же, как GrandChild, а для Child 'адресом будет 4 больше.

отметить также, что GrandChild::a является неParent::a ...

+0

Почему адрес 'Parent' такой же, как' GrandChild', я не понимаю. Пожалуйста, уточните –

+0

@VictorPolevoy - он * может * быть таким же, но он не * должен * быть. Обратите внимание, что 'Parent :: a' * * также * имеет тот же адрес, что и« Parent »и« GrandChild ». :-) Как правило, объекты разных типов * допускаются * иметь один и тот же адрес, потому что их все равно нельзя путать друг с другом. Два объекта ** одного типа ** должны иметь разные адреса. –

+0

@VictorPolevoy: Потому что это адрес, на котором начинается 4-байтовый под-объект 'Parent', _and_ адрес, с которого начинается весь n-байтовый объект' GrandChild'. Единственное место, где это становится «забавным», - это то, что адрес, по которому начинается 4-байтовый под-объект «Детский», отличается. –

0

Оказывается, что это зависит от последовательности, в которой Grandchild наследуется от родителя и ребенка.

С следующей последовательности я получил

struct GrandChild : public Parent, public Child { int a, b, c; }; 

Выход 1: адрес внуков является такой же, как адрес Родителей

GrandChild's address is at : 0x22fecc 
Child's address is at : 0x22fed0 
Parent's address is at : 0x22fecc 

Об изменении последовательности, в которой Grandchild наследуется от ребенка и родителя к

struct GrandChild : public Child , public Parent { int a, b, c; }; 

Выход 2: адрес GrandChilds совпадает с адресом ребенка

GrandChild's address is at : 0x22fecc 
Child's address is at : 0x22fecc 
Parent's address is at : 0x22fed0 
Смежные вопросы