Наследование защищенных членов переменных копий в память - PullRequest
0 голосов
/ 17 мая 2018

Мне сказали, что если я делаю наследование, когда базовый класс содержит защищенные переменные-члены, а дочерний класс создает базовый класс, он дублирует свои члены в памяти.Пример:

class Animal
{
public:
    virtual std::string& sound() { return m_sound; }
    virtual void setSound(std::string& sound) { m_sound = sound; }
    virtual int age() { return m_age; }
    virtual void setAge(int age) { m_age = age; }
protected:
    Animal(std::string sound, int age) : m_sound(sound), m_age(age) { }

    int m_age;
    std::string m_sound;
};

class Dog : public Animal
{
public:
    Dog(int speed) : Animal("Waff!!", 3), m_speed(speed) {}
    virtual int speed() { return m_speed; }
    virtual void setSpeed(int speed) { m_speed = speed; }
protected:
    int m_speed;
};

Итак, используя приведенный выше пример, что я имел в виду другими словами, что если я создаю объект Dog, он выделит 2 раза памяти для m_sound и m_age.
Это совершенно не имело для меня никакого смысла, и мне было интересно, почему компилятор сделал бы такую ​​вещь, если информация, которую я услышал, не вводит в заблуждение.
Так что в основном мы создаем:

Dog bulldog(5);

Я получил отладку этого кода и просмотр памяти:
enter image description here

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

1 Ответ

0 голосов
/ 17 мая 2018

Мне сказали, что если я делаю наследование, когда базовый класс содержит защищенные переменные-члены, а дочерний класс создает базовый класс, он дублирует свои члены в памяти.... он выделит в 2 раза больше памяти для m_sound и m_age.

Нет.В вашем примере каждый объект типа Dog содержит только два подобъекта int и один подобъект типа std::string.Технически компилятору будет разрешено использовать больше памяти, чем необходимо, но для этого не будет никаких причин, и программе не будет разрешено иметь разные адреса для одного и того же члена или несколько вызовов конструктора или деструктора std::string.

Не зная вашего источника, больше ничего не скажешь об этом.

Что именно распределяется в памяти, где отмечаются вопросы между данными родительского класса и потомком?

Подробности зависят от вашего компилятора и стандартной библиотеки, но, скорее всего, все или большинство байтов между тем, что вы определили как m_age и m_speed, принадлежат подобъекту члена std::string m_sound.То есть, я предполагаю, что если бы вы сделали std::cout << sizeof(std::string) << "\n";, вы бы получили 28 или близко к нему.

Из-за того, как работает объектная модель C ++, sizeof(std::string) является константой, и каждый std::string получает только фиксированное количество байтов, но строка может «содержать» много символов.Обычно это означает, что std::string имеет указатель char* на динамически распределенную память, которая содержит фактические символы.В общем случае также потребуется способ узнать длину строки и емкость динамически размещенного буфера.

Но, как вы заметили, символьные значения вашей строки фактически появляются в памяти std::string объект напрямую, что, вероятно, означает, что ваша стандартная реализация библиотеки использует Small String Optimization : std::string может содержать либо указатель char*, длину и емкость, либо определенное количество символьных значений напрямую.(Это может уменьшить потребность в динамическом выделении и освобождении памяти, что может занять много процессорного времени, если используется достаточно часто.) std::string также потребуется некоторый способ узнать, находится ли он в данный момент в режиме указателя или в режиме прямого хранения, иэта информация предположительно где-то в более поздних байтах.

Чтобы точно определить, что означает каждый байт в std::string, вам нужно будет найти определение шаблона класса std::basic_string в заголовочных файлах, используемых вашимкомпилятор.

...