c++学习笔记

来源:互联网 发布:网校用什么软件 编辑:程序博客网 时间:2024/06/05 10:04

**

堆和栈

**
1. 为什么使用指针

在操作大型数据和类时,指针可以通过内存地址直接访问数据,避免大量复制程序代码,因此执行效率最高。指针有三大用途:
1:处理堆中存放的大量数据。
2:快速访问类的成员数据和函数。
3:以别名的方式向函数传递参数。

2. 程序就是与数据打交道,在执行某一功能的时候,将该功能所需要的数据加载至内存,然后执行完毕后释放内存。
3. 数据在内存中存放的形式:
1. 栈区:由编译器自动分配并释放,该区域一般存放函数的参数值、局部变量的值。
2. 堆区:由程序员分配和释放,若程序员不释放,程序结束时可能有操作系统回收。
3. 寄存器区:用来保存栈顶指针和指令指针。自由度高
4. 全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。数据最不安全
5. 文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。
6. 程序代码区:存放函数体的二进制代码。
函数参数和局部变量存放在栈中,当函数运行结束并且返回时,所有局部变量和参数就都被系统自动清除掉了,为的是释放掉他们所占用的内存空间。全局变量可以解决这个问题,但是全局变量不会被释放,而且由于全局变量被所有的类成员和函数所共享,所以它的值很容易被修改。使用堆可以一举解决这两个问题。
接下来我们着重讨论下堆和栈之间的区别,通过两者之间的比较,我们可以明白为什么堆可以解决以上两个问题。
4.内存申请方式不同
4.1栈
由系统自动分配,例如我们在函数中声明一个局部变量int a;那么系统就会自动在栈中为变量a开辟空间。
4.2堆
需要程序员自己申请,因此也需要指明变量的大小。
5.系统响应的不同
5.1栈
只要栈的剩余空间大于所申请的空间,系统将为程序申请提供内存,否则将显示overflow,也就是栈溢出。
5.2堆
系统收到程序申请空间的要求后,会遍历一个操作系统用于记录内存空间空闲地址的链表,当找到一个空间大于所申请的空间的堆结点后,就会将该结点从记录内存空闲地址的的链表中删除。并将该结点的内存分配给程序,然后在这块内存区域的首地址记录分配的大小,这样我们在使用delete来释放内存的时候,delete才能正确的识别并删除该内存区域的所有变量。另外,我们申请的内存空间与堆结点的内存空间不一定相等,这时系统就会自动将堆结点上多出来的那一部分内存空间收回到空闲链表中。
6.空间大小的不同
6.1栈
在WINDOWS下,栈是一块连续的内存区域,它的大小是2M,总之该数值是一个在编译时就确定的常数。是有系统预先根据栈顶的地址和栈的最大容量定义好的。假如你的数据申请的内存空间超过栈的空间,那么就会提示overflow。因此,别指望栈能够存储比较大的数据。
6.2堆
堆是不连续的内存区域。各块区域由链表将它们串联起来,关于链表的知识将在后面的章节中讲解。这里只需要知道链表将各个不连续的内存区域连接起来,这些串联起来的内存空间叫堆,它的上限是由系统中有限的虚拟内存来定的。因此获得的空间较大,而且空间的方式比较灵活。
7.执行效率的不同
7.1栈
在函数调用时,第一个进栈的是被调用函数下一行的内存地址。其次是函数的参数,假如参数多于一个,那么次序是从右往左。最后才是函数的局部变量。
由于栈的先进后出原则,函数结束时正好与其相反,首先是局部变量先出栈,然后是参数,次序是从左到右,这时所有变量都已经出栈,指针自然地指到第一个进栈的那个内存地址,也就是被调用函数的下一行内存地址。程序根据该地址跳转到被调用函数的下一行自动执行。
7.2堆
堆是一大堆不连续的内存空间,在系统中由链表将它们串接起来,因此在使用的时候必须由程序员来安排。它的机制是很复杂的,有时候为了分配一块合适的内存,程序员需要按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有满足条件的空间,那么就要向系统发出申请增加一部分内存空间,这样就才有机会分到足够大小的内存,然后将计算后的数值返回。显然,堆运行效率要比栈低得多,而且容易产生碎片。但是好处是堆可以存储相当大的数据,并且这一细节可以由程序员来安排。

总结:

栈的内存小,但是效率高,不过存储数据只在函数内有效。超出函数就消失了。堆的可存储空间可非常大,但是容易产生内存碎片,效率也较低,好处是灵活性比较强。比如说我们需要创建一个对象,能够被多个函数所访问,但又不想使其成为全局的,那么这个时候创建一个堆对象无疑是良好的选择。
由于堆和栈各有优缺点,因此好多时候我们是将堆和栈结合使用的,比如在存储一些较大数据的时候,我们将数据存放在堆中,却将指向该数据的指针放到栈中。这样可以有效地提高执行速度,避免一些不该有的碎片。不过,一般来说,若不是较大数据,我们都是使用栈,比如:函数调用过程中的参数,返回地址和局部变量都存放在栈中。这样可以大大加快程序的运行速度。

8.用指针创建堆中空间
堆得好处是可以存储比较大的数据,而且存储的数据只要不是程序员手动将其释放那么就会永远保存在堆中。而栈中,存储的数据只在函数中有效,超出函数就消失了。也不想全局变量,保存的数据只有程序结束时才会释放,而且很容易被修改。
堆中每个内存单元都是匿名的,因此必须在堆中申请一个内存单元的地址,然后把它保存在指针中,这样你只有使用该指针才可以访问到该内存单元的数据。采用这种匿名的内存访问方式,而不是使用公开的全局变量,好处是只有特定的指针才能访问特定的数据。这样就避免了任何试图修改它的非法操作。
首先得创建一个堆,然后定义一个指向该堆得指针,这样就只能通过该指针才能访问堆中的数据。
在C++中采用关键字new创建一个堆并分配内存,在new后面跟着一个要分配的对象数据类型,编译器根据这个类型来分配内存,实例:int*p;p=new int
第一行定义了一个指向整形的指针变量p,第二行用new创建了一个int类型的内存区域,然后将该区域的内存地址赋给指针变量p。这样p所指向的就是这块新建的内存区域。在这里要注意的是,new int在堆中被编译器分配了4个字节的空间。假如new double那么就要被分配8个内存空间

9.
由于使用new创建的内存空间不会被系统自动释放,若不释放它,那么该块区域始终不能为其他数据所使用。而指向该内存的指针是个局部变量,当定义该指针的函数结束并返回时,指针也就消失了,那么就难以找到该块内存区域,将这种情况称之为内存泄漏。这种糟糕的情况只有程序运行结束时该块区域的内存才能使用。因此当不需要一块内存空间时,就必须对指向它的指针使用关键字delete。
Int *p=new int;delete p;不要连续删除
10.
This变量记录每个单独对象的内存地址,而this指针则指向每个单独的对象。因此不同对象的输出的this变量的内存地址也不同。This指针可以不写,编译器自动生成。
11.
常量指针自身不能改变,但是它指向的数据可以改变。
常量指针:int *const p//指针自身不可修改,指向的值可以修改。
指向常量指针:const int *p//指针自身可以修改,指向的常量的值不可修改。
指向常量的常指针:const int*const p//两者均不可修改。

12.引用
引用运算符&,用于为变量取别名。别名的地址和变量地址相同。定义:&ra=a
引用就是别名常量,其地址不变,其值可以发生改变。
指针类型的引用:
类型 *&q = p
13.函数重载,在相同作用域内,用同一函数名定义的多个函数,参数个数和参数类型不同。
内联函数,关键字:inline,递归函数无法实现内联方式,执行效率高,要求逻辑简单。只是一种编译方式。
14.内存管理
申请内存:new int *p=new int
释放内存:delete delete p;
int *arr = new int[10];delete []arr;
释放内存完之后需要设置空指针。
15.c++中的访问限定符
Public:公共的,protected:受保护的,private:私有的