草庐IT

c++ - 类成员和成员函数内存位置

coder 2024-02-15 原文

这是一个简单的 C++ 类,名为 A:

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 内存中的位置,它们可以指向程序二进制文件中的位置吗?如果是,这是如何工作的?

任何人都可以向我解释这是如何工作的,并指出我所知道的是对还是错?

最佳答案

首先,您需要了解 linker 的作用什么是executables (通常在 virtual memory 中执行)和 address spaces & processes .在 Linux 上,阅读 ELFexecve(2)系统调用。另请阅读 Levine 的 Linkers & Loaders书和Operating Systems: Three Easy Pieces , 和 C++11 标准 n3337 , 和 this draft报个好C++ programming书,与this reference网站。

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

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

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

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

  • A virtual member function通常通过 vtable ( virtual method table ) 实现。具有某些虚拟成员函数(包括析构函数)的对象通常将指向此类表(由编译器在其他地方生成)的指针作为其第一个(隐式)成员字段。你的 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++)你可以传递它,例如-fdump-tree-all 标志:它将产生数百个转储文件,部分显示 - 以转储文本形式 - 编译器的一些内部表示,您可以使用寻呼机检查它们(例如 less) 或文本编辑器。您也可以使用 MELT或查看使用 g++ -S -fverbose-asm -O1 ....

生成的汇编代码

关于c++ - 类成员和成员函数内存位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22119360/

有关c++ - 类成员和成员函数内存位置的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  4. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  5. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  8. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  9. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  10. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

随机推荐