当前位置:   article > 正文

《C++(二)--智能指针》_智能指针数组

智能指针数组

C++ 智能指针

智能指针行为类似于指针的类对象,可帮助管理动态内存。通俗的讲,智能指针的对象是一种指针,可以将new获得的地址赋给这种对象。当智能指针过期时,其析构函数将使用delete来释放内存。

参考:C++ Primer Plus P100-107
 

 
 

一、 new和delete

1.1 C++指针

指针其实也是一个变量,存储的是值的地址,*运算符被称为间接值或解除引用运算符。例如有指针变量pointer,则pointer是一个地址,*pointer表示存储在该地址的值。(&运算符表示取地址的值或者引用)
 

1.1.1 指针的声明和初始化

  • 可以在声明语句中初始化:
int a = 5;
int *pointer = &a;
  • 1
  • 2

这句话的意思是将a的地址的值存放在pointer变量中,可以通过*pointer来对变量进行操作。

  • 直接将数字作为地址赋给指针变量
int *pt;
pt = (int*)0xB80000000
  • 1
  • 2

其中,0xB80000000是一段偏移地址,要把这个数字作为地址来使用,需要对这其进行强制类型转换,利用(int*)转换成适当的地址类型。
 

1.1.2 指针的危险使用情况

在声明指针时,会为指针变量分配内存地址,但是不会为指针指向的数据分配内存,所以如果只是声明指针却不初始化指针,就直接利用指针来进行赋值操作,就会发生错误,因为指针不知道具体指向哪,也就是所说的野指针,这样是非常危险的。

int *pt;
*pt = 8;
  • 1
  • 2

指针不进行初始化,那么它可以是任何值,所以修改的pt指向那块地址的值很可能不是要存储我们赋给它的值,它甚至可以修改程序的任意位置的值。
 

1.2 利用new动态分配内存

在C语言中,可以使用malloc()函数来分配内存,在C++中,也可以使用malloc()函数来分配,但它有更好的方法----new运算符。

int *pt = new int;
  • 1

new会根据类型来确定一定字节的内存,然后在堆(heap)中分配,将这个内存块的地址返回给pt。new分配的内存块和常规变量声明分配的内存块不同,常规变量被存储在栈(stack)中,new从堆(heap)和自由存储区(free store)中分配。(如果无法分配,则会返回0,空指针(nullptr))。

当new针对类的对象分配时,这时候会执行构造函数。
 

1.3 利用delete释放内存

当我们不需要内存时,我们可以使用delete运算符释放内存,delete后接指针:

int *pt = new int;
...
delete pt;
  • 1
  • 2
  • 3

释放pt指向的内存,但是不会删除指针本身,可以利用new重新分配内存。另外,当内存已经释放时,不能再次释放。

delete pt;
delete pt;//不能再次释放
  • 1
  • 2

再者,deletenew是配套使用:

int a=5;
int *pn = &a;
delete pn;//非法语句,指向的内存不是用new分配在堆上的
  • 1
  • 2
  • 3

所以,只能用delete来释放new分配的内存。

对于面向的类对象而言,在new分配内存的时候,就会执行构造函数;在delete释放内存时,会调用析构函数来释放内存。
 

1.4 利用new和delete管理动态数组

声明创建数组会在程序编译时创建内存,不管使不使用,都会占用内存,属于静态联编;如果使用new,在运行阶段创建数组,在程序运行时选择数组长度,属于动态联编。

int *pt = new int[10];//创建动态数组
delete [] pt;//释放内存
  • 1
  • 2

两者是配套使用的,如果只使用了对于普通类型的指针仅仅使用delete pt,而不使用delete [] pt,虽然程序不会报错,但不建议这样使用;对于数组对象而言,这样的做法就是错误的,一定要配套使用delete [];

使用原则

  • 不要用delete释放不是new分配的内存;

  • 不要用delete释放内存两次;

  • 如果使用new []分配内存,应使用delete []释放内存;

  • 空指针对于delete是安全的;
     
     

二、 智能指针

当一个内存被new创建时,若没有使用delete来释放内存,则会导致内存泄露,C++的指针是非常高效的工具同时也是非常危险的;为了避免内存泄露必须配套的使用newdelete;为此,C++的智能指针能够更高效的管理指针,可以不使用delete来释放内存,更加安全高效。(auto_ptr,unique_ptr,shared_ptr)其中,auto_ptr已经不再使用。

参考:C++ Primer Plus P667-P673 (智能指针)
 

2.1 shared_ptr

使用智能指针,需要包含头文件 < memory > ,创建智能指针对象:

std::shared_ptr<double> pt(new double);
  • 1

所有的智能指针都有一个explicit构造函数,可以把指针作为参数,因此不需要自动将指针转换为智能对象:

//分别创建一个智能指针对象和普通指针
std::shared_ptr<double> pd;
double *pn = new double;
//指针操作
pd = pn; //非法,这是一种隐式转换,而构造函数是声明为explicit的
std::shared_ptr<double> pshared = pn;//也是非法,理由同上

pd = std::shared_ptr<double>(pn);//合法
std::shared_ptr<double> pshared(pn);//合法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

shared_ptr的策略:
采用引用计数,当赋值时,计数加1,指针过期时,计数减1,当最后一个指针过期时,才调用delete,同样也适合拷贝构造函数。
 

2.2 unique_ptr和auto_ptr

该智能指针保证了内存块的指针唯一性,若将unique_ptr的指针赋值给其他指针时,在编译的过程中便会报错。(编译报错相比于程序运行崩溃更加安全,更加容易发现)

std::unique_ptr<double> pn(new double);
std::unique_ptr<double> pt;
pt = pn;
//编译器会报错
  • 1
  • 2
  • 3
  • 4

但对于auto_ptr而言,这样的语句是合法的。

std::auto_ptr<double> pn(new double);
std::auto_ptr<double> pt;
pt = pn;
  • 1
  • 2
  • 3

pn的控制权会交由ptpn的所有权被剥夺,但如果程序后依然使用pn,它不能指向有效数据,则会导致程序崩溃;同时这样的操作会导致出现悬挂指针pn。相比于auto_ptr而言,unique_ptr更加安全。

auto_ptrunique_ptr都只能用new创建,不能使用new [],但对于shared_ptr,则可以使用new []delete []
 

 

2.3 make_shared

make_shared会返回一个shared_ptr的对象,它不需要使用new关键字,只分配一次内存,效率较高,减少了内存分配的开销。

所以尽量可以使用make_shared来创建。
但其缺点是,当类构造函数是保护成员或私有成员时,不能使用make_shared.

std::shared_ptr<string> pt;
pt = std::make_shared<string>("hello");
  • 1
  • 2

 
 

2.4 智能指针数组

可以对指针数组的创建:

std::unique_ptr<Eigen::Vector2d[]>  points(new Eigen::Vector2d[100]);
  • 1

或者利用boost库:

boost::shared_array<Eigen::Vector2d> points(new Eigen::Vector2d[100]); 
  • 1

利用shared_ptr:(在C++17以后才支持)

std::shared_ptr<int[]> p(new int[100]);
  • 1

如果编译器不支持,可以采用下列方式:

std::shared_ptr<int> p(new int[100], [](int* p) {delete[] p; });
std::shared_ptr<int> p(new int[100], std::default_delete<int[]>());
  • 1
  • 2

 


具体参考:
https://www.jianshu.com/p/03eea8262c11

https://blog.csdn.net/zhgeliang/article/details/81237053

https://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const?rq=1

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/781620
推荐阅读
相关标签
  

闽ICP备14008679号