- 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.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
的拷贝构造函数和拷贝构造 *** 作符实际上被删除了。
shared_ptr
是共享指针,它的工作方式是通过引用计数,可以跟踪我们的指针有多少个引用,一旦引用计数达到0,这个指针指向的内存就会被释放。
在unique_ptr
中不直接调用new
而使用make_unique
的原因是因为异常安全,但是在shared_ptr
中有所不同。
shared_ptr
需要分配另一块内存,叫做控制块,用来存储引用计数。
如果使用 std::shared_ptr
的方式,则过程是先创建一个new Entity
分配一次内存,然后将其传递给shared_ptr
构造函数再对控制块分配一次内存,这样它必须做两次内存分配。
若使用make_unique就可以把它们组合起来做一次内存分配,提高性能。
如下面的代码,解释shared_ptr
如果工作。
这里有两个作用域,在外面的作用域中有e0
,里面的作用域中有sharedEntity
,然后把e0
赋值为sharedEntity
。
在27行设置断点按F5运行,再按F10发现Entity
被创建了。
当里面的作用域死亡时,sharedEntity
就会死亡,但是e0
还存活并且持有对该Entity
的引用,所以这里没有调用析构函数。
若再按F10Entity
就会被销毁。
当所有的引用都消失,当所有的栈分配对象,追踪shared_ptr
的从内存释放后,底层的Entity
才会被删除(调用析构函数)。
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.1.浅拷贝和深拷贝笔记里基本都在写浅拷贝和深拷贝的内容,实际上没有什么新的东西。
就是在类中存在指针成员变量的时候,并且这个指针是指向堆上的内存。
如果使用浅拷贝,那么复制的时候就是仅仅复制指针,而不会复制指针指向的内存的数据。
这样当前一个对象被释放的时候,其指针成员变量一并被释放,导致这块内存无效。
而后一个对象的指针成员变量还是指向堆上的这块内存,当后一个对象被释放的时候它再次尝试释放堆上的这块内存,从而报错。
但如果按回车键,代码执行完cin.get()之后,代码就会崩溃。
Cherno在视频中说,在基础使用中尽量用常量引用来传递对象,尽管某些情况下可能复制对象更快。
常量引用除了减少拷贝之外,还有其他的好处。
如下代码,只会输出一句"Copied String"
,因为只在second=string
的时候调用了拷贝构造函数,在PrintString
函数传参的时候并没有进行拷贝。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)