адрес изменяется, когда я назначаю указателю указатель из производного класса в C ++ - PullRequest
2 голосов
/ 17 июня 2020

Я новичок в C ++ и экспериментирую с полиморфизмом. У меня есть следующий код:

#include <iostream>

class Base1
{
protected:
    int b1;

public:
    int m() { return 1; }
};

class Base2
{
protected:
    int b2;

public:
    int n() { return 2; }
};

class Der : public Base1, public Base2
{
protected:
    int d1;

public:
    int m() { return 11; }
    int n() { return 21; }
};

int main()
{
    Der *ptr = new Der();
    Base1 *b1 = ptr;
    Base2 *b2 = ptr;

    std::cout << "d: " << ptr << ", m: " << ptr->m() << ", n: " << ptr->n() << "\n";
    std::cout << "b1: " << b1 << ", m: " << b1->m() << "\n";
    std::cout << "b2: " << b2 << ", n: " << b2->n() << "\n";

    delete ptr;
    return 0;
}

Когда я запускаю этот код, интересно то, что b2 сдвигается на 4 байта, вот мой вывод:

d: 0x564eab6cbeb0, m: 11, n: 21
b1: 0x564eab6cbeb0, m: 1
b2: 0x564eab6cbeb4, n: 2

Почему это происходит только с Би 2? Я предполагаю, что это связано с тем, как вещи хранятся в памяти, потому что, если я удалю поле int в b1, b2 не изменится. Есть ли способ легко увидеть стек и кучу? Я бы хотел посмотреть, что будет. (Я использую код виртуальной студии)

Ответы [ 2 ]

2 голосов
/ 17 июня 2020

Пример OP (немного упрощенный)

struct Base1 {
  int b1;
};

struct Base2 {
  int b2;
};

struct Der: Base1, Base2 { };

может привести к следующему макету памяти:

// start of Der
// start of Base1
0000: Base1::b1 // type int
// start of Base2
0004: Base2::b2 // type int

Итак, когда struct Der является экземпляром части его содержимого является экземпляром struct Base2, но он не начинается с того же адреса, что и экземпляр Der.

С

Der *ptr = new Der();

инициализация

Base2 *b2 = ptr;

не приводит к простой копии адреса от ptr до b2. Также присутствует неявное преобразование из Der* в Base2*. Компилятору известно о соотношении классов Der и Base2. Следовательно, преобразование приводит к беззвучному добавлению смещения Base2 в Der.

Чтобы показать это в действии, я сделал небольшую демонстрацию. (Я не уверен, насколько это убедительно.):

#include <iostream>

struct Base1 {
  int b1 = 1;
};

struct Base2 {
  int b2 = 2;
};

struct Der: Base1, Base2 { };

int main()
{
  Der *ptr = new Der;
  Base2 *b2;
  std::cout << "ptr:" << ptr << ", ptr1->b2: " << ptr->b2 << '\n';
  b2 = ptr;
  std::cout << "b2: " << b2 << ", b2->b2: " << b2->b2 << '\n';
}

Скомпилировано с помощью g cc 4.1.2, вы можете найти следующий код, в котором происходит фактическое присвоение:

        mov     %rax, QWORD PTR [%rbp-24] # %rbp-24 <- storage of ptr on stack
        add     %rax, 4
        mov     QWORD PTR [%rbp-32], %rax # %rbp-32 <- storage of b2 on stack

Живая демонстрация в CompilerExplorer

Примечание:

При компиляции с использованием самой современной версии компилятора будет выдано аналогичное add, но также и множество других вещей, которые не так просто расшифровать (на глаз), чем сгенерированный код более старой версии. Следовательно, я выбрал самый старый gcc, который смог найти.

1 голос
/ 17 июня 2020

Да, это связано с тем, как вещи хранятся в памяти.

Класс Der включает Base1 и Base2 в качестве подобъекта.

Посмотрите это производный_класс .

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