繁体   English   中英

类成员和成员函数的内存位置

[英]Class members and member functions memory location

这是一个简单的 C++ 类:

class A
{
public:
    explicit A() : m_a(0) { }
    explicit A(int a) m_a(a) { }
    int getA() const { return m_a; }
    void setA(int a) { m_a = a; }

private:
    int m_a;
}

这是我目前所知道的:

  • 当您声明类实例的对象时,会为该对象分配内存。 分配的内存相当于其成员的内存总和。 所以在我的情况下sizeof(A) = sizeof(int) = sizeof(m_a)
  • 类 A 的所有成员函数都存储在内存中的某个位置,并且类 A 的所有实例都使用相同的成员函数。

这是我知道的:

成员函数存储在哪里以及它们实际上是如何存储的? 例如,假设一个int存储在 4 个字节上; 我可以想象具有 4 个连续单元的 RAM 内存布局,每个单元存储该 int 的一部分。 我怎么能想象一个函数的这种布局?(这听起来很傻,但我想函数必须在内存中占有一席之地,因为你可以有一个指向它们的指针)。 此外,功能指令是如何以及在哪里存储的? 我的第一个感觉是函数和函数指令存储在程序可执行文件(及其动态或静态库)中,但如果这是真的,当您创建函数指针时会发生什么? AFAIK 函数指针指向 RAM 内存中的位置,它们可以指向程序二进制文件中的位置吗? 如果是,这是如何工作的?

谁能向我解释这是如何工作的,并指出我所知道的是对还是错?

首先,您需要了解链接器的作用以及什么是可执行文件(通常在虚拟内存中执行)以及地址空间进程 在 Linux 上,阅读有关ELFexecve(2)系统调用的信息。 另请阅读 Levine 的链接器和加载程序书籍和操作系统:三个简单的部分,以及 C++11 标准n3337 ,以及这份报告草稿和一本优秀的C++ 编程书籍,以及此参考网站。

成员函数可以是虚函数或普通函数。

  • 一个普通(非virtual )成员函数就像一个 C 函数(除了它把this作为一个隐式的,通常是第一个参数)。 例如,您的getA方法的实现类似于以下 C 函数(在对象之外,例如在二进制可执行文件的代码段中):

     int C$getA(A*thisptr) const { return thisptr->m_a; }

    然后想象编译器正在将p->getA()翻译成C$getA(p)

  • 虚成员函数通常通过一个vtable虚方法表)来实现。 具有一些虚拟成员函数(包括析构函数)的对象通常将指向此类表(由编译器在别处生成)的指针作为其第一个(隐式)成员字段。 您的class A没有任何虚拟方法,但想象一下它是否有一个额外的virtual void print(std::ostream&); 方法,那么您的class A将具有与

    struct A$ { struct A$virtualmethodtable* _vptr; int m_a; };

    虚拟表可能是

    struct A$virtualmethodtable { void (*print$fun) (struct A$*, std::ostream*); };

    (因此添加其他虚函数意味着简单地在该vtable 中添加插槽); 然后调用p->print(std::cout); 将被翻译成几乎像p->_vptr.print$fun(p,&std::cout); ...此外,编译器将生成各种虚拟方法表(每个类一个)作为常量表。

注意:使用多重继承或虚拟继承,事情会更加复杂。

在这两种情况下,成员函数都不会占用对象中的任何额外空间。 如果它是非虚拟的,它只是一个普通的函数(在代码段中)。 如果它是虚拟的,它共享虚拟方法表中的一个槽。

注意。 如果您使用最近的GCC (即使用g++ )或使用Clang (因此clang++clang++您可以传递它,例如-fdump-tree-all标志:它将产生数百个转储文件,部分显示 - 以转储的文本形式 -编译器的一些内部表示,您可以使用寻呼机(例如less )或文本编辑器进行检查。 您还可以使用MELT或查看使用g++ -S -fverbose-asm -O1 ... 生成的汇编代码。

所有局部非静态变量和非虚函数都保存在代码/文本段中。

所有静态和全局变量都保存在静态数据段中。

具有虚函数的类或从具有虚函数的类继承的类将被编译器插入一个 vptr 指针。 Vptr 指向一个具有多个函数槽的虚函数表。 每个槽包含存储在代码段中的函数地址。

要理解这一点,您需要了解程序的内存布局。 代码将由对象共享。 并且所有对象都有自己的数据副本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM