#c #oop #inheritance
#c #ооп #наследование
Вопрос:
У меня возникли проблемы с пониманием значения this
в следующем примере:
struct A {
int i;
void bar() {
cout << this << endl;
}
};
struct B : public A {
virtual void foo() = 0;
};
struct C : public B {
void foo() {
printf("hello world!n");
}
};
int main (int argc, const char* argv[]) {
C* c = new C;
cout << c << endl;
c->bar();
return 0;
}
Оба раза, когда я печатаю указатель на консоль, я получаю разные значения. Я ожидал бы, что это будет то же самое, поскольку они оба раза ссылаются на один и тот же экземпляр ?!
Если я удалю либо виртуальную функцию, либо int i
in A
, она исчезнет. Почему?
Ответ №1:
Но это относится к разным вещам.
void bar() {
cout << this << endl;
}
Здесь this
есть тип A*
. Он указывает на ту часть объекта, которая есть A
.
out << c << endl;
Здесь c
a C*
и указывает на ту часть объекта, которая есть C
.
Если A
объект совпадает точно с C
объектом, указатели совпадают. Если они этого не делают (например, когда C содержит другие элементы (скрытый указатель на vtable) и, следовательно, часть «A» смещена от начала более крупного объекта), то указатели не обязательно совпадают.
Комментарии:
1.Обычно, даже если
C
у него есть дополнительные члены, они будут одинаковыми. Однако, еслиC
есть какие-либоvirtual
функции, обычно их ставят перед любыми членами.2. звучит разумно, однако я действительно удивлен. есть ли какая-либо возможность получить
this
класс, который был изначально создан?3. Для дополнительного удовольствия попробуйте добавить это в
main
:A* c2 = c; cout << c2 << endl;
4. @lucas clemente: Да, dynamic_cast<C>(this) вернет родительский указатель (если это C или NULL, если это не C).
5. @Лукас Клементе: Правильно. Способ обойти это — иметь виртуальный деструктор в A. Это заставит деструктор подключиться к своему родительскому классу перед началом уничтожения.
Ответ №2:
Это оптимизация компилятора. Структура A не имеет никаких виртуальных методов, поэтому ей не нужна v-таблица. Нет смысла хранить указатель на него в объекте типа A. Что делает макет объекта различным для объектов типа A и C, поскольку C нуждается в v-таблице. Компилятор компенсирует разницу, увеличивая это значение перед вызовом метода A.
Добавьте произвольный виртуальный метод в A, чтобы сопоставить указатели.
Комментарии:
1. Даже с виртуальным методом указатели не совпадают, я только что попробовал.
2. Работает на моей машине ™. В любом случае имеет смысл для меня.