class TestClass
{
public:
void setNum(int num)
{
m_num1 = num;
}
int getNum()
{
return m_num1;
}
private:
int m_num1;
int m_num2;
};
#include "pch.h"
#include <iostream>
#include "mytest.h" int main()
{
TestClass test;
test.setNum(); printf("sizeof testClass=%d,num = %d\n", sizeof(test), test.getNum()); std::cout << "Hello World!\n";
}

输出:sizeof testClass=8,num = 100

没有虚函数时,test变量在内存中的分布

若存在虚函数

class TestClass
{
public:
virtual void setNum(int num)
{
m_num1 = num;
}
virtual int getNum()
{
return m_num1;
}
private:
int m_num1;
int m_num2;
};

我们用IDA打开看一下反汇编

ext:004127B0 var_D8          = byte ptr -0D8h
.text:004127B0 var_14 = byte ptr -14h
.text:004127B0 var_4 = dword ptr -
.text:004127B0
.text:004127B0 push ebp
.text:004127B1 mov ebp, esp
.text:004127B3 sub esp, 0D8h
.text:004127B9 push ebx
.text:004127BA push esi
.text:004127BB push edi
.text:004127BC lea edi, [ebp+var_D8]
.text:004127C2 mov ecx, 36h
.text:004127C7 mov eax, 0CCCCCCCCh
.text:004127CC rep stosd
.text:004127CE mov eax, ___security_cookie
.text:004127D3 xor eax, ebp
.text:004127D5 mov [ebp+var_4], eax
.text:004127D8 mov ecx, offset unk_41E009
.text:004127DD call sub_411299
.text:004127E2 lea ecx, [ebp+var_14] // ecx 保存this指针,通过this指针地址偏移来调用类成员
.text:004127E5 call sub_411311
.text:004127EA push
.text:004127EC lea ecx, [ebp+var_14]
.text:004127EF call sub_41113B
.text:004127F4 lea ecx, [ebp+var_14]
.text:004127F7 call sub_4112A8
.text:004127FC push eax
.text:004127FD push 0Ch
.text:004127FF push offset aSizeofTestclas ; "sizeof testClass=%d,num = %d\n"
.text: call sub_411055
.text: add esp, 0Ch
.text:0041280C push offset Str ; "Hello World!\n"
.text: mov eax, ds:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text: push eax ; int
.text: call sub_411226

对象作为返回值
1、分配一个临时对象空间 main_object;
2、把临时对象入栈
3、调用返回局部对象的函数

在返回局部对象的函数里面
fun_object;
一些局部变量操作
返回的时候用用局部对象作为参数,传入main_object对象的this指针
调用复制构造函数
用EAX返回this指针

tt=main_object;

一下转自:https://blog.csdn.net/qq_22660775/article/details/89854545

C++规定当函数返回的是非引用类型时,函数会创建临时对象(temporary object),函数返回的就是这个临时对象。在求解表达式时,如果需要一个地方存储其运算结果,编译器会创建一个没有命名的对象,这就是临时对象。浅显的说,当你调用了函数,函数会 return一个值 那么这个值总得有存放的地方吧,编译器就把会把值存放在一个没有命名法临时对象中。

我们举个例子来说明一下,首先定义一个类:

class B {
public:
B(){
cout << "B的构造函数" << endl;
} B(int i){
cout << "带int型参数的B的构造函数" << endl;
} B(const B &ano){
cout << "B的复制构造函数" << endl;
} B& operator=(const B& rhs){
cout << "B的赋值操作符" << endl;
return *this;
} virtual ~B(){
cout << "B的析构函数" << endl;
}
};

定义一个函数:

B func2()
{
B b;
return b;
}

上面的函数返回一个非引用类型的变量,我们写两个测试函数,来看看返回一个非引用类型的变量会发生什么

void test1() {
B t;
t = func2();
} void test2() {
B t = func2();
}

运行测试函数test1(),其运行结果为:

B的构造函数 //构造主方法内的对象t
B的构造函数 //构造fun2内的局部对象b
B的复制构造函数 //将func2的局部对象复制到一个临时对象
B的析构函数 //析构局部对象b
B的赋值操作符 //使用临时对象初始化t
B的析构函数 //析构临时对象
B的析构函数 //析构对象t

由于 t 的初始化采用的是operator=操作符,operator=要求必须使用一个已经创建好了的对象对左值进行复制,所以此时必须先形成一个临时对象,然后将临时对象赋值给 t

运行测试函数test2(),其运行结果为:

B的构造函数 //构造fun2内的局部对象b
B的复制构造函数
B的析构函数
B的析构函数

由于 t 是通过复制构造函数进行初始化的。复制构造函数初始化要求左值是一个已有对象,而非创建好了的对象,因此此时不需要创建一个临时对象

C++对象内存布局,this指针,对象作为参数,作为返回值的更多相关文章

  1. c++ 指针做为参数和返回值

    指针参数 返回值是指针 一.指针作参数形式的函数 //计算x的平方 x*x void square(int *x) { int a=*x; *x=a*a; } 二.指针作返回值的函数 int *squ ...

  2. Java对象内存布局

    本文转载自Java对象内存布局 导语 首先直接抛出问题 Unsafe.getInt(obj, fieldOffset)中的fieldOffset是什么, 类似还有compareAndSwapX(obj ...

  3. JVM-对象及对象内存布局

    目录 前言 类与对象 对象类二分模型 对象 对象内存布局 JOL工具 对象头 Mark Word 类型句柄 对象头与锁膨胀 无锁 偏向锁 轻量级锁 重量级锁 重量级锁降级 实例数据 填充 对象生命周期 ...

  4. 图说C++对象模型:对象内存布局详解

    0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看. 本文的结论都在VS2013上得到验证.不同的编译器在内存布局的细节上可能有 ...

  5. c++ 对象内存布局详解

    今天看了的,感觉需要了解对象内存的问题.参考:http://blog.jobbole.com/101583/ 1.何为C++对象模型? 引用<深度探索C++对象模型>这本书中的话: 有两个 ...

  6. c++对象内存布局

    这篇文章我要简单地讲解下c++对象的内存布局,虽然已经有很多很好的文章,不过通过实现发现有些地方不同的编译器还是会有差别的,希望和大家交流. 在没有用到虚函数的时候,C++的对象内存布局和c语言的st ...

  7. 从C++对象内存布局和构造过程来具体分析C++中的封装、继承、多态

    一.封装模型的内存布局 常见类对象的成员可能包含以下元素: 内建类型.指针.引用.组合对象.虚函数. 另一个角度的分类: 数据成员:静态.非静态 成员函数:静态.非静态.虚函数 1.仅包含内建类型的场 ...

  8. c++对象内存布局的理解

    我对c++对象内存布局的理解   引言 结合网上的一些资料,通过自己的一番摸索,得出了一点个人见解.现在写下来,希望与各位同学共同探讨,共同进步. 以下所有代码均是在VS2012下测试. 一个普通的基 ...

  9. 好文章系列C/C++——图说C++对象模型:对象内存布局详解

    注:收藏好文章,得出自己的笔记,以查漏补缺!     ------>原文链接:http://blog.jobbole.com/101583/ 前言 本文可加深对C++对象的内存布局.虚表指针.虚 ...

  10. 使用sos查看.NET对象内存布局

    前面我们图解了.NET里各种对象的内存布局,我们再来从调试器和clr源码的角度来看一下对象的内存布局.我写了一个测试程序来加深对.net对象内存布局的了解: using System; using S ...

随机推荐

  1. vue路由懒加载及组件懒加载

    一.为什么要使用路由懒加载 为给客户更好的客户体验,首屏组件加载速度更快一些,解决白屏问题. 二.定义 懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载. 三.使用 常用的懒加载方式 ...

  2. Docker之数据卷(Data Volumes)操作

    目的: 前言 Docker宿主机和容器之间文件拷贝 数据卷 数据卷容器 前言 Docker 数据管理 在生产环境中使用 Docker ,往往需要对数据进行持久化,或者需要在多个容器之间进行 数据共享, ...

  3. pyspider 数据存入Mysql--Python3

    一.不写入Mysql 以爬取哪儿网为例. 以下为脚本: from pyspider.libs.base_handler import * class Handler(BaseHandler): cra ...

  4. 【华为敏捷/DevOps实践】7. 敏捷,DevOps,傻傻不分清楚【华为云技术分享】

    文:姚冬(华为云DevCloud首席技术布道师,资深DevOps与精益/敏捷专家,金融解决方案技术Leader,中国DevOpsDays社区核心组织者) 前言 敏捷是什么?DevOps是什么?两者有什 ...

  5. .net Dapper 实践系列(4) ---数据查询(Layui+Ajax+Dapper+MySQL)

    写在前面 上一小节,总结了数据显示时,会出现的日期问题.以及如何处理格式化日期.这个小节,主要总结的是使用Dapper 中的QueryMultiple方法依次显示查询多表的数据. 实践步骤 1.在Bo ...

  6. sdcard不可执行.

    Possibly you placed it on your sdcard -- which is mounted with the noexec flag. You either need to m ...

  7. 解决java依赖poi导出Excel表时,没有出现下载提示的问题

    转自:https://blog.csdn.net/jinchunzhao123/article/details/88626077 浏览器响应: 而且进入断点调试,所有的数据都执行了就是没有下载提示.而 ...

  8. Android为TV端助力之QQ空间热更新技术

    直接上代码 package com.enjoy.patch; import android.content.Context;import android.os.Build;import android ...

  9. jsonpath 一个简单实用的工具

    import jsonpath import json data = "{\"a\": \"11\", \"c\": {\&quo ...

  10. 【DATAGUARD】物理dg配置客户端无缝切换 (八.1)--Data Guard Broker 的配置

    [DATAGUARD]物理dg配置客户端无缝切换 (八.1)--Data Guard Broker 的配置 一.1  BLOG文档结构图       一.2  前言部分   一.2.1  导读 各位技 ...