我试图通过创建大量对象来了解我将会遇到什么样的内存.我知道每个对象 - 在创建时 - 将在HEAP中为成员变量提供空间,并且我认为属于该类型对象的每个函数的所有代码都存在于内存中的代码段中 - 永久存在.
是对的吗?
因此,如果我在C++中创建100个对象,我可以估计我将需要空间用于对象拥有的所有成员变量乘以100(这里可能的对齐问题),然后我需要代码段中的空间用于单个副本的该类型对象的每个成员函数的代码(不是代码的100个副本).
虚拟函数,多态,继承因素以某种方式进入吗?
动态链接库中的对象怎么样?我假设dll获得自己的堆栈,堆,代码和数据段.
简单示例(可能在语法上不正确):
// parent class class Bar { public: Bar() {}; ~Bar() {}; // pure virtual function virtual void doSomething() = 0; protected: // a protected variable int mProtectedVar; } // our object class that we'll create multiple instances of class Foo : public Bar { public: Foo() {}; ~Foo() {}; // implement pure virtual function void doSomething() { mPrivate = 0; } // a couple public functions int getPrivateVar() { return mPrivate; } void setPrivateVar(int v) { mPrivate = v; } // a couple public variables int mPublicVar; char mPublicVar2; private: // a couple private variables int mPrivate; char mPrivateVar2; }
关于动态分配Foo类型的对象需要多少内存,包括代码和所有变量的空间?
"每个对象 - 在创建时 - 将在HEAP中为成员变量提供空间"并不一定正确.您创建的每个对象都会在某处为其成员变量占用一些非零空间,但是您可以在何处分配对象本身.如果对象具有自动(堆栈)分配,则其数据成员也是如此.如果对象是在免费存储(堆)上分配的,那么它的数据成员也是如此.毕竟,除了数据成员之外,对象的分配是什么?
如果堆栈分配的对象包含指针或其他类型,然后用于在堆上进行分配,则无论对象本身在何处创建,该分配都将发生在堆上.
对于具有虚函数的对象,每个对象都将分配一个vtable指针,就像它是类中显式声明的数据成员一样.
至于成员函数,那些代码的代码可能与自由函数代码在可执行映像中的位置没有区别.毕竟,成员函数基本上是一个自由函数,其隐含的"this"指针作为其第一个参数.
继承不会改变任何东西.
我不确定你对DLL获取自己的堆栈的意思.DLL不是程序,并且不需要堆栈(或堆),因为它分配的对象总是在具有自己的堆栈和堆的程序的上下文中分配.在DLL中存在代码(文本)和数据段确实有意义,尽管我不是在Windows上实现这些东西的专家(我假设你使用的是你的术语).
代码存在于文本段中,并且基于类生成多少代码相当复杂.没有虚拟继承的无聊表面在文本段中表面上只有一个代码用于每个成员函数(包括在省略时隐式创建的那些,例如复制构造函数).正如您所说,任何类实例的大小通常是成员变量的总和大小.
然后,它变得有些复杂.一些问题是......
如果需要或被指示,编译器可以使用内联代码.因此,即使它可能是一个简单的函数,如果它在许多地方使用并被选择用于内联,则可以生成大量代码(遍布程序代码).
虚拟继承增加了每个成员的多态大小.VTABLE(虚拟表)使用虚方法隐藏类的每个实例,其中包含运行时调度的信息.如果您有许多虚函数或多个(虚拟)继承,则此表可能会变得非常大.澄清:VTABLE是每个类,但指向VTABLE的指针存储在每个实例中(取决于对象的祖先类型结构).
模板可能会导致代码膨胀.使用带有一组新模板参数的模板化类可以为每个成员生成全新的代码.现代编译器尽可能地尝试崩溃,但这很难.
结构对齐/填充可能导致简单的类实例比预期的更大,因为编译器填充了目标体系结构的结构.
编程时,使用sizeof运算符来确定对象大小 - 从不硬编码.在估算大型实例组的成本时,请使用"成员变量大小的总和+某些VTABLE(如果存在)"的粗略度量,并且不要过于担心代码的大小.稍后进行优化,如果任何非显而易见的问题再次发生意义,我会感到非常惊讶.