c++对象模型

释放双眼,带上耳机,听听看~!

栈(stack) VS. 堆(heap)

  • 由系统自动管理,以执行函数为单位
  • 空间大小编译时确定(参数+局部变量)
  • 函数执行时,系统自动匹配一个stack
  • 函数执行结束,系统立即自动回收stack

反例–悬浮指针:


1
2
3
4
5
6
1myclass * func(){
2   myclass c(10);
3   return &c; //返回栈对象的地址!
4}
5
6

1
2
3
4
5
6
7
8
9
1myclass func(){
2   myclass c(10);
3   aclass a(100);
4
5   c.pa = &a;
6   return c;//返回c会调用拷贝构造函数,aclass是栈对象,c.pa会发生悬空指针
7}
8
9

指向栈对象的指针是非常危险的!!!
还有一种危险操作是用堆对象的指针指向栈对象

  • 在c++中由程序员手动控制

  • 手动分配new和malloc

  • 手动释放delete和free

  • 具有全局性,总体无大小限制

  • 容易造成内存泄漏


1
2
3
4
5
6
7
8
1myclass * func(){
2   myclass * pa = new myclass();
3   return pa;
4}
5
6myclass * p = func(); //接收指针的人不一定知道要delete掉这个指针
7
8

返回值尽量不要返回指针:

  • 返回栈对象指针是错误
  • 返回堆对象指针:接收指针的人要负责delete掉这个指针,这和谁分配谁释放相悖。

堆对象的空间分析

c++对象模型
堆对象:栈上存储指针,堆上存储真正的对象
关键点:要会画内存模型

栈对象的空间分析

c++对象模型
栈对象:对象内存直接存储于栈空间

变量模型与使用

  • 三种变量模型

  • 对象 myclass c;

    • 指针 myclass * pc; (不要和解引用混淆c=*pc;)
    • 引用 myclass & c2 = c; (不要和取地址混淆pc=&c;)
  • 三种使用场景

  • 声明对象

    • 传参
    • 返回值

在堆上的变量要么是指针表示要么是引用表示:


1
2
3
4
1myclass * pc2 = new myclass();//指针表示堆对象
2myclass& c3 = *pc2; //引用表示堆对象
3
4

传参:
by value / by pointer/ by reference


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1void func1(myclass c){ //对象往往很大,值传递会发生拷贝
2 //对象的大小依赖于其含有的字段、虚函数、对齐等
3}
4
5void func2(myclass *pc){
6// 传指针从拷贝成本上是可以的。但是也有它的问题。
7// 一般不建议传指针。
8// 1. 栈指针和堆指针看不出来
9// 2. 要不要在函数里delete掉(栈指针delete会出现行为未定义;堆对象也不应该是你来delete,谁分配谁释放)
10}
11
12void func3(myclass& mc){ //
13//传引用相对安全
14}
15
16

例子:谁分配谁释放:


1
2
3
4
5
1myclass * pc = new myclass();
2func2(pc);
3delete pc;
4
5

反例:


1
2
3
4
5
6
1void func3(myclass & mc){
2   myclass *p = &mc;
3   delete p;//有危险,mc并不是函数内分配的,不应该在函数内释放!
4}
5
6

引用在行为上和值传递不完全一样(引用在函数内部修改,函数外部也会变),怎么办?
const引用–值传递的替代品!

但凡这个类型比int大,传引用都有价值。


1
2
3
4
5
6
1myclass c1;
2func(c1);   //调用拷贝构造
3func2(&c1); //没有调用拷贝构造
4func3(c1);  //没有调用拷贝构造
5
6

例子–返回值(两个方式):


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1myclass func1(){
2   myclass c1;
3   return c1; //方式1
4   myclass * pc2 = new myclass();
5   return *pc2;  //方式2,出现内存泄漏
6}
7
8myclass c = func1(); //c是一个新的对象,是一个拷贝
9//pc2被丢掉了,出现了内存泄漏
10//内存泄漏是发生在进程范围内的
11//自己运行看不出来,因为运行一会就关了
12//在服务器上,一个用户运行泄漏一点(比如40byte),几天几个月服务器就会死掉
13
14

例子–返回指针(两种方式):


1
2
3
4
5
6
7
8
1myclass* func2(){
2   myclass c1;
3   return &c1; //方式1,返回栈对象的地址,悬空指针
4   myclass *pc2 = new myclass();
5   return pc2; //方式2,有内存泄漏的危险,违反谁分配谁释放
6}
7
8

慎用返回指针!

例子–返回引用:


1
2
3
4
5
6
7
8
9
10
11
12
1myclass& func3(){
2   myclass c1;
3   return c1;//返回栈对象引用,错误
4   myclass *pc2 = new myclass();
5   return *pc2;//违反谁分配谁释放
6}
7
8myclass c4 = func3();//发生拷贝构造,pc2指向的堆对象会内存释放
9myclass& c4 = func3(); //语法正确,但是极少这么使用
10delete &c4;
11
12

*this是可以返回的,因为其1. 生存周期很长;2. this怎么传进来怎么返回去,合理


1
2
3
4
5
1myclass& func4(myclass& c){
2   return c;
3}
4
5

能不能只有栈没有堆?不能

  1. 栈是编译时就要确定的,具有静态特征;
  2. 堆具有全局性,具有灵活性,读取1000000个数据

能不能只有堆没有栈?

  1. 没有栈就没有函数了,所有函数的状态、操作数,变量都没了。
  2. 堆是需要栈指针来指向的。

给TA打赏
共{{data.count}}人
人已打赏
安全技术

node.js – JWT有效负载应该有多少信息?

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索