继承受保护的成员变量在内存中复制

时间:2018-05-16 22:23:46

标签: c++ inheritance virtual-method memory-layout

我被告知,如果我在基类包含受保护的成员变量时继承,并且子类将构造基类,它将在内存中复制它的成员。例如:

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对象,它将为m_sound和m_age分配2倍的内存。
这完全没有任何意义,我一直在想为什么编译器会做这样的事情,除非我听到的信息具有误导性。
所以说主要是我们创建:

Dog bulldog(5);

调试此代码并查看内存是我得到的:
enter image description here

我想知道在父类数据和孩子之间存在问题标记的内存区域中究竟分配了什么。

1 个答案:

答案 0 :(得分:2)

  

我被告知,如果我在基类包含受保护的成员变量时继承,并且子类将构造基类,它将在内存中复制它的成员。 ......它会为m_soundm_age分配2倍的内存。

不。在您的示例中,Dog类型的每个对象只包含两个int子对象和一个std::string类型的子对象。从技术上讲,编译器可以使用超过必要的存储空间,但没有理由,并且不允许程序为同一成员或多个std::string构造函数或析构函数调用提供不同的地址。

在不知道你的来源的情况下,对此没什么好说的。

  

在内存中分配父类数据与子类之间的问题究竟是什么?

详细信息取决于您的编译器和标准库,但很可能在您标识为m_agem_speed之间的所有或大部分字节都属于std::string m_sound成员子对象。也就是说,我猜测如果你做std::cout << sizeof(std::string) << "\n";,你会得到28或接近它。

由于C ++对象模型的工作方式,sizeof(std::string)是一个常量,每个std::string只获得固定数量的字节,但字符串可以包含&#34;很多人物。这通常意味着std::string有一个char*指针指向动态分配的内存,该内存保存实际字符。通常还需要一种方法来了解字符串的长度和动态分配的缓冲区的容量。

但是正如您所注意到的,字符串的字符值实际上直接出现在std::string对象的内存中,这可能意味着您的标准库实现正在使用Small String Optimization:{{1} }可以包含std::string指针和长度和容量,或直接包含一定数量的字符值。 (这可以减少动态分配和释放内存的需要,如果经常使用,可能需要大量的CPU时间。)char*还需要某种方式来了解它当前是否处于指针模式或直接存储模式,并且该信息可能位于后面的字节中。

要准确解释std::string中每个字节的含义,您需要在编译器使用的头文件中查找类模板std::string的定义。