C ++ указатель мульти-наследование весело - PullRequest
9 голосов
/ 28 января 2010

Я пишу некоторый код, включающий наследование от базового класса указателей подсчета ссылок; и некоторые тонкости C ++ всплыли. Я уменьшил это следующим образом:

Предположим, у меня есть:

class A{};
class B{};
class C: public A, public B {};

C c;
C* pc = &c;
B* pb = &c;
A* pa = &c;

// does pa point to a valid A object?
// does pb point to a valid B object?

// does pa == pb ?

Кроме того, делает:

// pc == (C*) pa ?
// pc == (C*) pb ?

Спасибо!

Ответы [ 5 ]

8 голосов
/ 28 января 2010
  • указывает на действительный объект A?
  • pb указывает на действительный объект B?

Да, C* преобразуется так, что pa и pb указывают на правильные адреса.

  • делает pa == pb?

Нет, обычно нет. Не может быть объекта A и объекта B по одному адресу.

Кроме того,

  • pc == (C *) в год?
  • pc == (C *) pb?

Приведение преобразует указатели обратно в адрес объекта C, поэтому оба равенства верны.

2 голосов
/ 28 января 2010

C встраивает A и B.

class C: public A, public B {};

очень похоже на код C

struct C {
    A self_a;
    B self_b;
};

и (B*) &c; эквивалентно static_cast< B* >( &c ) аналогично &c.self_b, если вы использовали прямую C.

Как правило, вы не можете полагаться на указатели на различные типы, которые являются взаимозаменяемыми или сопоставимыми.

1 голос
/ 28 февраля 2017

Элемент 28 Значение сравнения указателей в Общие знания C ++: основное промежуточное программирование ) объясняет ключ указателя объекта в C ++:

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

Посмотрите на код:

class A{};
class B{};
class C: public A, public B {};

C c;
C* pc = &c;
B* pb = &c;
A* pa = &c;

class C является производным от class A и class B, поэтому class C равно class A и class B. объект C c имеет 3 действительных адреса: адрес для class A, class B и class C. Реализация зависит от компилятора, поэтому вы не можете предположить расположение памяти class C, и это может выглядеть так:

 ----------  <- pc (0x7ffe7d10e1e0)
 |        |
 ----------  <- pa (0x7ffe7d10e1e4)
 | A data |
 ----------  <- pb (0x7ffe7d10e1e8)
 | B data |
 ----------
 | C data |
 ----------

В приведенном выше случае, хотя значения адресов pc, pa и pb не одинаковы, они все ссылаются на один и тот же объект (c), поэтому компилятор должен убедиться, что pc сравнивается равным pa и pb, т. е. pc == pa и pc == pb. Компилятор выполняет это сравнение, корректируя значение одного из сравниваемых указателей с помощью соответствующего смещения. Например,

pc == pa

переводится на:

pc ? ((uintptr_t)pc + 4 == (uintptr_t)pa) : (pa == 0)

Помимо прочего, поскольку A и B не имеют отношений наследования, мы не можем сравнивать pa и pb напрямую.

На ваши вопросы:

(1) does pa point to a valid A object?  
(2) does pb point to a valid B object?  
Yes, refer the above diagram. 

(3) pc == (C*) pa ?  
(4) pc == (C*) pb ?  
Yes, No need to add (C*).

(5) does pa == pb ?
No. We can't compare them.
1 голос
/ 28 января 2010
pc == pa;
pc == pb;

Не определено, зависит от структуры класса.

pc == (C*) pa;
pc == (C*) pb;

Все в порядке.

pa == pb;

Нет.

Они указывают на действительные объекты?

Yes
0 голосов
/ 28 января 2010

То, что вы получаете, это что-то вроде этого в памяти

 ----------
 | A data |
 ----------
 | B data |
 ----------
 | C data |
 ----------

Так что, если вам нужен весь объект C, вы получите указатель на начало памяти. Если вам нужна только «часть», вы получите тот же адрес, поскольку там находятся члены данных. Если вы хотите B "part", вы получите начало + sizeof (A) + sizeof (все, что компилятор добавляет для vtable). Таким образом, в этом примере pc! = Pb (может быть pc! = Pa), но pa никогда не равен pb.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...