基于实际项目的C++存储区体验
情况介绍
最近做实验的过程中,大量接触C++代码,在修改代码的过程中,体验到了先前课程上学习过,但是不理解的种种行为,所以想做一个记录
为什么要使用指针
Python和Java在开辟一个变量或对象后,所赋的值其实是该变量或对象的地址如
Python:
a = A()
Java:
B b = new B();
但是在C++中,生成新对象时,可以直接获取这个对象实体:
C c();
访问其对象时,可以直接通过.
进行访问,比如c.getData()
,这也是在先前课程中最喜欢使用的方式,但是在大型的项目中,同样使用该方式,则会出现问题。
在C++中,传递一个变量有两种方式
-
直接拷贝整个对象实体,进行传递
-
传递该变量的指针,即传递其在存储空间中的地址
显而易见,传递一个4Byte
的指针要比传递可能上千行代码的对象实体要来的快。如果类似函数赋值等操作,很容易考虑到效率,选择传递该对象的指针,或者该对象的引用(C++的特性),来加速这一过程。但是还是有一些地方,容易忽略导致代码效率变低且难以寻找bug。
-
变量赋值:在遍历vector数组时,同时需要获取数组下标,可能采用如下写法:
vector<tree> treeList; for(int i=0;i<treeList.size();i++) { tree t = treeList[i]; ··· }
这种方式,看上去没有什么问题,但是在实际项目中,tree对象中有上千行代码,这就使得该方式效率变得十分低下,所以需要将tree类型的vector转换成指针类型。
-
for循环的语法糖:利用C++11推出的新特性,我们编写for循环时可以使用如下写法:
vector<tree> treeList; for(auto treeObj : treeList) { ··· }
这样通过不断迭代,在每次循环中,可以直接使用treeObj作为数组中的对象,但是该方式仍然为拷贝对象,编译器实际执行的是创建一个新的tree对象,从数组中拷贝到该对象上,效率十分低下,好在C++提供了使用其引用的方式:
vector<tree> treeList; for(const auto & treeObj : treeList) { ··· }
存储区管理
C++的存储区分成五大部分:
- 栈
- 堆
- 全局变量
- 字符串常量
- 代码区
我们在代码中开辟的变量占用了其中四个存储区,全局变量和字符串常量很好理解,但是关于堆栈则需要进行具体的理解。如果直接声明变量:
void f() {
A a;
···
}
那么该变将会被存放在栈中,其有效生命周期自其声明开始,直到该变量外层的第一个大括号结束为止,声明周期结束后,会自动将其清理出栈。所以在使用指针的过程中需要时刻考虑其所指的变量或对象是否还在栈中,如果已经出栈则会出现Runtime Error
。那有没有办法持续的使用一个变量,不受外层{}
限制呢?
这就需要用到堆,在C++中,声明一个变量并存放在堆中,可以使用如下方式:
A *a = new A();
但是需要注意,C++堆中的变量是不会自己清空的,即没有垃圾回收机制,需要人为使用delete方式进行清除,否则持续的开辟堆变量,可能会耗尽堆的存储资源,导致程序运行错误。