Cherno C++系列笔记14——P44~P45 智能指针、复制与拷贝构造函数

Cherno C++系列笔记14——P44~P45 智能指针、复制与拷贝构造函数,第1张

文章目录
  • 1.智能指针
    • 1.1.unique_ptr(优先使用,开销低)
    • 1.2.shared_ptr
    • 1.3.weak_ptr
  • 2.P45 复制与拷贝构造函数
    • 2.1.浅拷贝和深拷贝
      • 2.1.1.浅拷贝示例
      • 2.1.2.深拷贝示例
    • 2.2.总是(always)用常量引用传递对象

1.智能指针

参考:视频 笔记

1.1.unique_ptr(优先使用,开销低)

unique_ptr是作用域指针,超出作用域时它会被销毁,然后自动调用delete


为什么叫做unique独一无二的?因为我们不能复制一个unique_ptr,因为如果复制一个unique_ptr会有两个指针,两个unique_ptr指向同一个内存块。


如果其中一个死了,它会释放那段内存,而另一个unique_ptr指针就会指向被释放的内存,这样这个指针就是无效的。


因此当你想要使用一个作用域指针的时候unique_ptr是最优选择。


要访问智能指针,首先要包括memory头文件,即#include


如果想要在特定的作用域下(两个大括号)创建一个unique_ptr来分配Entity,可以调用构造函数然后输入new Entity()


但是注意下面的写法有错误,因为unique_ptr的构造函数声明了explicit,即禁止了隐式类型转换,需要显示调用构造函数。


#include 
#include 

class Entity
{
public:
	Entity()
	{
		std::cout << "Construct Entity!" << std::endl;
	}
  ~Entity()
  {
  	std::cout << "Destroyed Entity!" << std::endl;
  }
  void Print()
  {
  	// Print
  }
};

int main()
{
	{
		// 1.错误写法:隐式类型转换
		std::unique_ptr<Entity> entity=new Entity();
		
		// 2.正确写法:显式调用构造函数
		std::unique_ptr<Entity> entity(new Entity());
		
		// 创建智能指针后就可以像普通指针一样使用它
		entity->Print();
	}
	
	std::cin.get();
}

一个更好的方法是把entity赋值给std::make_unique,主要是因为异常安全。


如果构造函数碰巧抛出异常,使用make_unique(C++14)会保证最终得到的不是没有引用的悬空指针,从而造成内存泄漏。


std::unique_ptr<Entity> entity = std::make_unique<Entity>();
entity->Print();

之所以unique_ptr不能进行复制,是因为unique_ptr的拷贝构造函数和拷贝构造 *** 作符实际上被删除了。



1.2.shared_ptr

shared_ptr是共享指针,它的工作方式是通过引用计数,可以跟踪我们的指针有多少个引用,一旦引用计数达到0,这个指针指向的内存就会被释放。



  
unique_ptr中不直接调用new而使用make_unique的原因是因为异常安全,但是在shared_ptr中有所不同。


shared_ptr 需要分配另一块内存,叫做控制块,用来存储引用计数。


如果使用 std::shared_ptr entity(new Entity);的方式,则过程是先创建一个new Entity分配一次内存,然后将其传递给shared_ptr构造函数再对控制块分配一次内存,这样它必须做两次内存分配。


若使用make_unique就可以把它们组合起来做一次内存分配,提高性能。


如下面的代码,解释shared_ptr如果工作。


这里有两个作用域,在外面的作用域中有e0,里面的作用域中有sharedEntity,然后把e0赋值为sharedEntity


在27行设置断点按F5运行,再按F10发现Entity被创建了。


当里面的作用域死亡时,sharedEntity就会死亡,但是e0还存活并且持有对该Entity的引用,所以这里没有调用析构函数。


若再按F10Entity就会被销毁。


当所有的引用都消失,当所有的栈分配对象,追踪shared_ptr的从内存释放后,底层的Entity才会被删除(调用析构函数)。


1.3.weak_ptr

weak_ptr被称为弱指针,可以和shared_ptr一起使用。


它只是像声明其他东西一样声明,可以给它赋值为sharedEntity


这里和之前复制sharedEntity所做的一样,但是这里不会增加引用计数。


当我们将一个shared_ptr赋值给另外一个shared_ptr时它会增加引用计数。


但是当把一个shared_ptr赋值给一个weak_ptr时不会增加引用计数。



  
如果不想要Entity的所有权,例如在排序一个Entity列表时不关心它们是否有效,只需要存储一个它们的引用就可以了。


我们可能会问weak_ptr底层对象是否还存活,但它不会保持底层对象存活,因为它不会增加引用计数。



  
若把之前的shared_ptr换成一个weak_ptr


在27行设置断点按F5运行,再按F10发现Entity在这里被创建,然后它会被分配到sharedEntity


但当退出里面的作用域时就是它被摧毁的时候,所以这个weak_ptr现在指向一个无效的Entity。


2.P45 复制与拷贝构造函数

参考:视频 笔记

2.1.浅拷贝和深拷贝

笔记里基本都在写浅拷贝和深拷贝的内容,实际上没有什么新的东西。


就是在类中存在指针成员变量的时候,并且这个指针是指向堆上的内存。


如果使用浅拷贝,那么复制的时候就是仅仅复制指针,而不会复制指针指向的内存的数据。


这样当前一个对象被释放的时候,其指针成员变量一并被释放,导致这块内存无效。


而后一个对象的指针成员变量还是指向堆上的这块内存,当后一个对象被释放的时候它再次尝试释放堆上的这块内存,从而报错。


2.1.1.浅拷贝示例

但如果按回车键,代码执行完cin.get()之后,代码就会崩溃。



2.1.2.深拷贝示例

2.2.总是(always)用常量引用传递对象

Cherno在视频中说,在基础使用中尽量用常量引用来传递对象,尽管某些情况下可能复制对象更快。


常量引用除了减少拷贝之外,还有其他的好处。


如下代码,只会输出一句"Copied String",因为只在second=string的时候调用了拷贝构造函数,在PrintString函数传参的时候并没有进行拷贝。



欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/562847.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存