智能指针
智能指针出现的背景
定义于头文件memory中
| 指针类型 | 描述 |
|---|---|
| unique_ptr | 拥有独有对象所有权的智能指针 |
| share_ptr | 拥有共享对象所有权的智能指针 |
| weak_ptr | std::share_ptr管理对象的弱引用 |
| auto_ptr | c++17已经移除,拥有严格对象所有权语义的智能指针 |
这些都是模板类
摘选自gfg(阿三网站)
#include<iostream>
using namespace std;
class RectAngle{
int length;
int breadth;
};
void fun(){
RectAngle *p = new RectAngle(); //创建一个RectAngle指针指向一个新的RectAngle的内存空间
//delete p; 需要手动的去释放指针
}
int main(){
while(1){
fun(); //不断的创建指向RectAngle的指针变量
}
}
程序运行结果

可以看到,由于不断地积累迭代,局部变量指针并不会消亡,而是保存在那里,然而新的RectAngle又在不断生成新的,这就造成了旧的没有释放,而新的不断在堆内存中生成,这就造成了内存泄漏地现象,而在创建后面加一个free§或者加一个delete p,可以避免这个问题,但是程序员往往会忘了在写代码时加一个释放空间的声明,所以为了更好的为程序员偷懒,C++11推出的智能指针应运而生
加了释放空间声明后的运行结果

So what happens is it’ll have a pointer ‘p’ and this will be pointing to an object of type rectangle which will have length and breadth. Once the function ends this ‘p’ is deleted because p is a local variable to the function which will end but a new rectangle that is allocated inside heap that will not be deallocated. And it will return it will come back as its infinite loop again it will call, so again new p is created then again a new object is created for rectangle same length and breadth. So then what about the previous object, it’ll not be deleted, again extra new object, it’ll also not deleted. So every time it will create an object but not it’s deleting so this is causing leakage of memory from the heap memory. Like for memory for length and breadth is unused though it’s allocated but not in use. So slowly the entire heap memory may become unused because it’s infinite. So at one stage because of a lack of heap memory, the program will crash. So at the last of the fun() we should use ‘delete p’ if we do not mention this, this will cause a very severe problem. so because of the laziness or carelessness of the programmer this type of problem may arise. So to help the programmer C++ 11 takes responsibility and introduces smart pointers
Introduction of Smart Pointers in C++ and It’s Types - GeeksforGeeks
The problem with heap memory is that when you don’t need it you must deallocate itself. So mostly the programmers are too lazy in writing the code for deallocation of objects and that causes severe problem like memory leak which will cause the program to crash. So the languages like Java, C#, .Net Framework they provide a garbage collection mechanism to deallocate the object which is not in use. So in C++ 11, it introduces smart pointers that automatically manage memory and they will deallocate the object when they are not in use when the pointer is going out of scope automatically it’ll deallocate the memory.
大致意思是为了解决程序员有时候忘记释放变量或者指针的空间时,造成不必要的类似于堆内存溢出的情况,设计了一个自动管理变量内存的智能指针,这个智能指针的内存管理不需要程序员来手动管理内存空间,它会帮助程序员来自动管理,像Java,C#,.NET都有类似的垃圾回收机制,这大大帮助了程序员的偷懒!,是编程界的一大喜讯啊!,当然这段翻译是自己的理解,读者也可以根据原文去阿三网站那边看看!
可以看到
- 对于传统的指针定义必须使用delet运算符或者free来释放内存防止出现内存溢出
typename *p = ....;
delete p; //必须使用delete运算符或者free手动的来释放内存空间
- 而智能指针则不需要!
Using Smart Pointers, we can make pointers to work in a way that we don’t need to explicitly call delete(不需要借助delete运算符来释放空间). A smart pointer is a wrapper class(包装类) over a pointer with an operator like * and -> overloaded.(类似于*运算符的重载) The objects of smart pointer class look like a pointer but can do many things that a normal pointer can’t like automatic destruction(自动销毁) (yes, we don’t have to explicitly use delete), reference counting and more.
源于GFG
class SmatePtr{
int *ptr;
template <typename T>
class SmatePtr{
T *ptr;
public:
SmatePtr(T t){
ptr = &t;
}
~SmatePtr(){
delete(ptr); //释放指针空间
}
T& operator*(){ //重载*运算符
return *ptr;
}
};
int main(){
SmatePtr<int> ptr(3);
*ptr = 20;
//无需进行delete ptr操作,因为这个操作在析构函数中早就已经帮我们办好了
return 0;
}
Note: Smart pointers are also useful in the management of resources,(十分有利于资源的管理) such as file handles(文件句柄) or network sockets(网络编程).
unique_ptr
std::unique_ptr 是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针,
当指针1指向一个对象时,那么这个对象的所有权都在这个指针手里,其它的指针在这个指针释放这个对象的控制权之前无法得到这个对象的控制权
If you are using a unique pointer then if one object is created and pointer P1 is pointing to this one them only one pointer can point this one at one time. So we can’t share with another pointer, but we can transfer the control to P2 by removing P1.
#include<iostream>
#include<memory>
using namespace std;
class RectAngle{
int length;
int width;
public:
RectAngle(int l,int w):length(l),width(w){}
int area(){
return length*width;
}
};
int main(){
//独特指针的构造方式
unique_ptr<RectAngle> pt1(new RectAngle(4,5)); //指向长度为4,宽度为5的一个矩形对象
unique_ptr<RectAngle> pt2 = make_unique<RectAngle>(4,3); //通过make_unique来构造一个unique_ptr
//在pt1和pt2释放对对象的控制权之前,其他指针无法获得对对象的控制权
cout<<"The area of the RectAngle which pointed by the pt1 is:"<<pt1->area()<<endl;
cout<<"The area of the RectAngle that pointed by the pt2 is:"<<pt2->area()<<endl;
//unique_ptr<RectAngle> pt3 = pt1; 这样的赋值方法是不行的,因为这是独享指针,不可能存在两个指针同时指向同一个位置
//通过move来释放控制权,并且赋值
unique_ptr<RectAngle> pt3 = move(pt1);//此时释放了pt1对对象的控制权,pt1变成了一个nullptr
if(pt1==NULL){
cout<<"The point of the pt1 are set to NULL"<<endl;
}
return 0;
}
这里小总结以下吧
unique_ptr是一个独有指针包装类,它独立的占有指向对象的控制权,没有释放控制权时,其他指针无法指向这个对象
- 构造unique_ptr
unique_ptr可以管理单个和多个对象,一个通过
new,一个通过new []分配等等,可能还有其它的分配方法
- 释放控制权,将控制权交给下一个指针
通过move(ptr)来释放unique_ptr对对象的控制权,并且通过move之后,之前的unique_ptr会被置为nullptr
关于智能指针,其实还有很多说明,可以参照
std::unique_ptr - cppreference.com
shared_ptr
shared_ptr从它的名字中可以看到,它指向对象的控制权是可以共享的
If you are using shared_ptr then more than one pointer can point to this one object at a time and it’ll maintain a Reference Counter(引用计数) using use_count() method(这个use_count方法可以看到有多少个share_ptr指针指向这个对象).

上代码
//构造方式跟unique_ptr没有什么差异
#include<iostream>
#include<memory>
using namespace std;
class RectAngle{
int length;
int width;
public:
RectAngle(int l,int w):length(l),width(w){}
int area(){
return length*width;
}
};
int main(){
//构造一个shared_ptr
shared_ptr<RectAngle> pt1(new RectAngle(3,4));
cout<<"The area of the RectAngle that pointed by the pt1 is:"<<pt1->area()<<endl;
cout<<"The count that point to the Object is:"<<pt1.use_count()<<endl;
//指针赋值
shared_ptr<RectAngle> pt2 = pt1; //可以直接通过赋值运算符运算,并且pt1不会置为NULL
cout<<"The area of the RectAngle that pointed by the pt2 is:"<<pt2->area()<<endl;
cout<<"The area of the RectAngle that pointed by the pt1 is:"<<pt1->area()<<endl;
cout<<"The count that point to the Object is:"<<pt1.use_count()<<endl;
return 0;
}
- shared_ptr在跳出作用域范围之后会自动释放自己的内存空间,这一特性是通过析构函数来实现的
- 如果shared_ptr所指向的对象如果没有其它的指针再次指向它的话,会自动释放对象的存储空间
讲到这里,兴起自己也写了一个简陋的智能指针
#include<iostream>
using namespace std;
class RectAngle{
int length;
int width;
public:
RectAngle(int l,int w):length(l),width(w){}
int area(){
return length*width;
}
//重载<<运算符
friend ostream &operator<<(ostream &os,RectAngle &rectAngle){
os<<"This is a RectAngle\n"<<"Length is:"<<rectAngle.length<<'\t'<<"Width is:"<<rectAngle.width<<std::endl;
os<<"The area of the RectAngle is:"<<rectAngle.width*rectAngle.length<<std::endl;
return os;
}
};
//定义的一个命名空间
namespace mysmartpoint{
template<typename ElemType>
class My_shared_ptr{
static int counter;
ElemType *p;
public:
My_shared_ptr(ElemType *elem){
counter++;
p = elem;
}
//拷贝构造函数
My_shared_ptr(My_shared_ptr &mySharedPtr){
p = mySharedPtr.p;
counter++;
}
//重载*运算符,对指针进行解引用
ElemType& operator*(){
return *p;
}
//重载=运算符
RectAngle& operator=(RectAngle &rectAngle){
return rectAngle;
}
//重载->运算符
ElemType* operator->(){
return p;
}
~My_shared_ptr(){
counter--;
if(counter==0){
cout<<"Successfully release\n";
delete p;
}
p = nullptr;
}
//类似于Reference counter
int my_ptr_count(){
return counter;
}
};
template<typename ElemType>
int My_shared_ptr<ElemType>::counter = 0;
}
int main(){
mysmartpoint::My_shared_ptr<RectAngle> pt1(new RectAngle(3,4));
mysmartpoint::My_shared_ptr<RectAngle> pt2 = pt1;
mysmartpoint::My_shared_ptr<RectAngle> pt3 = pt2;
mysmartpoint::My_shared_ptr<RectAngle> pt4 = pt1;
cout<<pt1->area()<<endl;
cout<<pt2->area()<<endl;
cout<<pt3->area()<<endl;
cout<<pt4->area()<<endl;
cout<<pt1.my_ptr_count()<<endl;
cout<<pt2.my_ptr_count()<<endl;
cout<<pt3.my_ptr_count()<<endl;
cout<<pt4.my_ptr_count()<<endl;
return 0;
}
在编写这段代码的时候笔者有在析构函数和重载<<运算符和在析构函数,下面我来分享以下我的笔记
在重载RectAngle的<<运算符时,首先我这样写的
ostream& operator<<(ostream &os,RectAngle &rectAngle){ os<<"This is a RectAngle\n"<<"Length is:"<<rectAngle.length<<'\t'<<"Width is:"<<rectAngle.width<<std::endl; os<<"The area of the RectAngle is:"<<rectAngle.width*rectAngle.length<<std::endl; return os; }但是它会报错,怎么想都不知道这是一个什么样的问题,然后我去stackoverflow上查资料,看到
然后我发现从答主的语言中得到一般<<是一个全局运算符,一般使用友元函数来重载这个流运算符,所以根据资料,我改了以下我的运算符操作
friend ostream& operator<<(ostream &os,RectAngle &rectAngle){ os<<"This is a RectAngle\n"<<"Length is:"<<rectAngle.length<<'\t'<<"Width is:"<<rectAngle.width<<std::endl; os<<"The area of the RectAngle is:"<<rectAngle.width*rectAngle.length<<std::endl; return os; }这样就写了一个类似于Java的toString了!可以直接通过<<运算符输出,具体更详细的原因我还在研究中哈哈哈
其次就是我在自己编写一个智能指针的时候,遇到了不可预期的错误,当时我的析构函数是这样写的
~My_shared_ptr(){ counter--; delete p; //释放指针p的内存空间 }//测试用例 int main(){ mysmatepoint::My_shared_ptr<RectAngle> pt1(new RectAngle(3,4)); mysmatepoint::My_shared_ptr<RectAngle> pt2 = pt1; mysmatepoint::My_shared_ptr<RectAngle> pt3 = pt2; mysmatepoint::My_shared_ptr<RectAngle> pt4 = pt1; cout<<pt1->area()<<endl; cout<<pt2->area()<<endl; cout<<pt3->area()<<endl; cout<<pt4->area()<<endl; cout<<pt1.my_ptr_count()<<endl; cout<<pt2.my_ptr_count()<<endl; cout<<pt3.my_ptr_count()<<endl; cout<<pt4.my_ptr_count()<<endl; return 0; }然后我通过测试用例运行这个项目,出现了这个问题
然后通过询问、查资料与分析,我发现我的析构函数写的有问题,这样的析构函数并不能达到一个智能指针的效果,首先是对于
delete这个运算符没有理解好,对于delete运算符,调用delete p;,照我的话来说,其实是抹去了这个指针变量指向的地址,而通过=赋值表达式赋值赋值的其它封装类的指针是与p指向的内存位置是相同的,这种情况下调用一次析构函数就会将指针指向的内存地址抹去**,而这些封装类足足调用了四次,第一次就已经将指针指向的地址抹去了,其它的的析构函数再次抹去的话会冗余,导致程序运行中不可避免地错误,这样在大型程序中是十分危险地!,所以基于delete的特性,我修改了以下析构函数~My_shared_ptr(){ counter--; if(counter==0){ delete p; } p = nullptr; }这样修改一下,运行这个程序就没有问题了
weak_ptr
它是基于share_ptr具有与share_ptr相似的功能,但是它不具有Reference Counter功能,即无法知道这个对象有几个指针指向它,为什么这样的原因为了防止不同的指针之间关联性太强而造成死锁现象。
It’s much more similar to shared_ptr except it’ll not maintain a Reference Counter.In this case, a pointer will not have a strong hold on the object. The reason is if suppose pointers are holding the object and requesting for other objects then they may form a Deadlock.
It does not maintain a Referenc Counter
智能指针是C++11引入的用于自动管理内存的对象,主要有unique_ptr、shared_ptr和weak_ptr三种类型。它们解决了传统指针可能导致的内存泄漏问题。unique_ptr拥有对象的独占所有权,离开作用域时自动释放;shared_ptr允许多个指针共享对象所有权,使用引用计数来管理内存,当引用计数为0时自动删除对象;weak_ptr是shared_ptr的辅助类型,不增加引用计数,用于解决循环引用问题。智能指针的使用大大简化了内存管理,降低了程序员的工作负担和程序出错的可能性。




5299

被折叠的 条评论
为什么被折叠?



