您的位置:首页 > 编程语言 > C语言/C++

C++基础知识部分之四

2017-12-27 20:03 260 查看
1、指针和动态内存

动态内存允许所创建的程序具有在编译时大小可变的空间。

1.1 堆栈

C++程序中的内存分为两部分——堆栈和堆。堆栈顶部通常是正在执行的函数。堆栈为每个函数提供独立的内存空间。在堆栈上分配内存的变量不需要由程序员释放内存,这个过程是自动完成的。

1.2 堆

堆是与当前函数或堆栈帧完全没有关系的内存区域。如果想要在函数调用结束之后仍然保存其中的变量,可以将变量保存到堆中。(操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码 中的delete语句才能正确的释放本内存空间。我们常说的内存泄露,最常见的就是堆泄露(还有资源泄露),它是指程序在运行中出现泄露,如果程序被关闭掉的话,操作系统会帮助释放泄露的内存。)

1.3 使用指针

明确的分配内存,就可以在堆上放置任何内容。例如,要在堆上放置一个整数,就需要给它分配整数大小的内存,首先需要声明一个指针:

int * aInteger;

int 后面的*表示变量名aInteger是一个指向某个存储了一个整数的内存的指针名。在任何时候都应该避免使用未初始化的变量,尤其是未初始化的指针,因为他们会指向内存中的任意一个位置,使用这种指针很可能使程序崩溃,因此应该显示同时声明和初始化指针。如果不希望立即分配内存可以把指针初始化为空指针nullptr:

int * aInteger = nullptr;

假设有如下程序:

int a=1;

int * aInteger = &a;

此时指针aInteger指向一个整数值的地址,要想访问这个值,需要对指针解除引用,即例如 cout<<*aInteger<<endl;

在解除引用之前指针必须有效,对null或者未初始化的指针解除引用会导致程序崩溃。

因此,在写程序的时候,解除引用之前最好判断这个指针是否是空指针。

当一个指针指向一个结构时,例如:

struct student

{

char firstname;

char lastname;

long studentnumeber;

}//学生信息的结构体

student * s1;//指向学生信息的结构体的一个指针

想要访问s1里面的额firstname可以使用:

(*s1).firstname;

也可以使用:

s1->firstname;

2. 动态分配数组

堆也可以用于动态分配数组,使用new[]操作符可以给数组动态分配内存:

int arraySize = 5;

int * myArray = new int[arraySize];

指针变量在堆栈中,动态创建的数组在堆中。

使用完这个数组后,应该将其从堆中删除,这样其他的变量也能使用这块内存。在C++中使用delete[]操作符完成这个任务,例如:

delete [] myArray;

在C++中,每次调用new时,都必须相应的调用delete,每次调用new[] ,都必须相应的调用delete[],避免造成内存泄漏。调用new[]时,只使用delete也会造成内存泄漏。

3. 空指针常量

在C++11以前,常量0用于表示整数0或者空指针,这有可能会造成一些问题,例如:

有两个函数名一样的函数,但是形参不同,

void func(char * str)

{

cout<<"char * version"<<endl;

}

void func(int i)

{

cout<<"int version"<<endl;

}

int main()

{

func(NULL);

return 0;

}

main()函数通过参数NULL调用func(),NULL是一个空指针常量,也就是说这个例子要用空指针作为实参调用char*版本的函数,但是实际上NULL不是指针而是整数0,所有实际调用的func()的整数版本。

这个问题可以引用真正的空指针nullptr来解决。

func(nullptr)。

4. 智能指针

为了避免常见的内存的问题,应该使用只能指针代替常见的C样式的裸指针。智能指针对象在超出作用域时,例如在函数执行完毕以后,会自动释放内存。

C++有三种智能指针:

std::unique_ptr

std::shared_ptr

std::weak_ptr

都在<memory>头文件中定义。

unique_ptr指针类似于普通指针,但是在unique_ptr超出作用域或者被删除时,会自动释放内存或资源。

unique_ptr只属于它指向的对象。

unique_ptr的一个优点是,发生异常情况,必须释放内存空间时,简化了编码。

智能指针离开其作用域时,会自动释放存储空间。

声明方式为:std::make_unique<>()创建unique_ptr指针。

例如,下面的代码:Employee * anEmployee = new Employee;

可以改写为:auto anEmployee = std::make_unique<Employee>();

unique_ptr是一个通用的智能指针,可以指向任意类型的内存,所以unique_ptr是一个模板。模板需要<>指定模板参数。在<>中必须指定unique_ptr要指向的内存类型。

shared_ptr允许数据的分布式“所有权”,说白了就是有多少地方在用它。每次指定shared_ptr时,都会递增一个引用计数(就是指明有几个地方在用它)。shared_ptr超出作用域时,就递减引用计数。当引用计数为0时,就表示数据不再有任何拥有者,于是释放指针引用的对象。不能在shared_ptr中存储数组。

使用std::make_shared<>()可以创建shared_ptr。

使用weak_ptr可以观察shared_ptr,而不会递增或者递减所链接的shared_ptr的引用计数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息