3. 类指针与智能指针
本篇主要解答以下问题:
- 类的对象与类指针的区别
- 为什么要用智能指针
- unique_ptr 和 shared_ptr
3.1 类对象与类指针
实例化一个类的时候,有两种方式,一是创建对象,二是创建指针。以2.1中的student类为例子:
int main() {
student s1;
student *s2 = new student();
}
new 产生的类指针与直接例化一个对象主要有三点不同:
-
内存空间与生命期不同
二者的类型决定了它们在内存上的分布不同。一个是对象类型,在创建时就已为对象分配好内存空间,用的是内存栈,是个局部的临时变量,作用域在该函数体内,随函数的结束被释放。一个是指针类型,用的是内存堆,是个永久变量,在调用时需要先用new分配动态内存,用完后必须手动delete掉。如不想手动delete也可使用智能指针。
-
调用方式上的不同
对象使用" . “操作符调用,而指针使用” -> "操作符调用。
-
访问和传递的不同
对象可直接访问,但不能实现多态,声明即调用了构造函数(已分配了内存);作为参数是,传递对象会调用拷贝构造函数,复制整个对象空间,参数传递占用资源大。
指针变量是间接访问,但可实现多态(通过父类指针可调用子类对象,以及子对象中重写的父类虚函数),并且没有调用构造函数。
3.2 智能指针
对象在生命周期结束后会自动盗用析构函数释放内存,类指针需要通过 delete 来调用析构函数进行销毁,那指向的普通数据的指针却不会自动销毁,可能导致C++内存泄漏。能否使得指针所指向的对象在生命周期结束后自动销毁呢?那就要使用智能指针,基于 CPP 14+ 这里主要介绍 unique_ptr 和 shared_ptr。
销毁对象
std::unique_ptr
unique_ptr 拥有它所指向的对象,在某一时刻,只能有一个unique_ptr指向特定的对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。在下列两者之一发生时用关联的删除器释放对象:
- 销毁了管理的 unique_ptr 对象
- 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。
std::shared_ptr
是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:
- 最后剩下的占有对象的 shared_ptr 被销毁;
- 最后剩下的占有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。
初始化写法
std::unique_ptr<student> spw(new student);
auto spw = std::make_unique<Widget>();
std::shared_ptr<student> spw(new student);
auto spw = std::make_shared<Widget>();
第一种执行了两次内存分配,第二种使用make函数。
make函数用来把一个任意参数的集合完美转移给一个构造函数从而生成动态分配内存的对象,并返回一个指向那个对象的指针,所以应该尽量使用 std::make_unique 和 std::make_shared 而不直接使用new。
std::move()的作用
unique_ptr 无法进行复制构造与赋值操作,但可以进行移动构造和移动赋值操作(即作为函数的返回值),这种情形类似于move() 操作。
unique_ptr<int> up(new int(88 );
unique_ptr<int> uPtr2 = std:move( up) ;
这里是显式的所有权转移,把up所指的内存转给uPtr2了,而up不再拥有该内存,可见move()的作用。