C++ 指针具有和 C 指针的功能,但是随着 C++ 的发展, C++ 引入了更多新特性
指针常量
在C++11之前都会使用NULL表示空指针,有时使用NULL定义为常量0会导致程序在运行时出现意外的结果。分析下面的重载函数
#include <iostream>
using namespace std;
void pfun(char *str) {cout<<"char* version"<<endl;}
void pfun(int i) {cout<<"int version"<<endl;}
int main(void)
{
pfun(NULL);
return 0;
}
编译无法通过:
[Error] call of overloaded ‘pfun(NULL)’ is ambiguous
正是因为NULL等价于常量0,又同时作为空指针。编译器不清楚该执行哪一个函数。为了解决这个问题,自从C++11之后引入了真正的空指针nullptr。将该指针替换NULL,程序就可以正常运行了。
char* version
智能指针
C++中有两种重要的智能指针分别是:std::unique_ptr和std::shared_ptr。我们以std::unique_ptr为例简单的介绍一下,unique_ptr类似于普通指针,只属于它指向的对象。C++给程序员编程提供了强大灵活性(尤其是指针操作),程序员需要提醒自己注意内存的释放,稍不注意就容易造成内存泄漏。unique_ptr 会自动释放资源,不用让程序员担心内存泄漏的问题。创建 unique_ptr,应当使用 std::make_unique<>()
- unique_ptr包装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。
#include <iostream>
#include <memory>
struct Task {
int mId;
Task(int id ) :mId(id) {
std::cout << "Task::Constructor" << std::endl;
}
~Task() {
std::cout << "Task::Destructor" << std::endl;
}
};
int main()
{
// 通过原始指针创建 unique_ptr 实例
std::unique_ptr<Task> taskPtr(new Task(23));
//通过 unique_ptr 访问其成员
int id = taskPtr->mId;
std::cout << id << std::endl;
return 0;
}
Task::Constructor
23
Task::Destructor
unique_ptr 将对象 taskPtr 接受原始指针作为参数。现在当main函数退出时,该对象超出作用范围就会调用其析构函数,在unique_ptr 对象 taskPtr 的析构函数中,会删除关联的原始指针,这样就不用专门 delete Task 对象了。这样不管函数正常退出还是异常退出(由于某些异常),也会始终调用taskPtr的析构函数。有效的避免内存泄漏。
- unique_ptr独享所有权
unique_ptr对象始终是关联的原始指针的唯一所有者。我们无法复制unique_ptr对象,它只能移动。由于每个unique_ptr对象都是原始指针的唯一所有者,因此在其析构函数中它直接删除关联的指针,不需要任何参考计数。- unique_ptr常用方法
-
创建unique_ptr对象
std::unique_ptr<int> ptr1; //创建一个空的unique_ptr<int>对象,因为没有与之关联的原始指针,所以它是空的 std::unique_ptr<Task> taskPtr(new Task(22)); //创建非空的 unique_ptr 对象,需要在创建对象时在其构造函数中传递原始指针 std::unique_ptr<Task> taskPtr(new std::unique_ptr<Task>::element_type(23)); //创建非空的 unique_ptr 对象 std::unique_ptr<Task> taskPtr2 = new Task(); // 编译错误,不能使用赋值的方法创建 std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34); //使用 std::make_unique 创建 unique_ptr 对象 / C++14
-
判空
// 方法1 if(!ptr1) std::cout<<"ptr1 is empty"<<std::endl; // 方法2 if(ptr1 == nullptr) std::cout<<"ptr1 is empty"<<std::endl;
-
获取被管理对象指针
Task *p1 = taskPtr.get();//使用get() 函数获取管理对象的指针
-
重置 unique_ptr 对象
taskPtr.reset(); //在 unique_ptr 对象上调用reset()函数将重置它,即它将释放delete关联的原始指针并使unique_
- 转移 unique_ptr 对象所有权
由于 unique_ptr 不可复制,只能移动。因此,我们无法通过复制构造函数或赋值运算符创建unique_ptr对象的副本。// 编译错误 : unique_ptr 不能复制 std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error // 编译错误 : unique_ptr 不能复制 taskPtr = taskPtr2; //compile error
我们无法复制 unique_ptr 对象,但我们可以转移它们。这意味着 unique_ptr 对象可以将关联的原始指针的所有权转移到另一个 unique_ptr 对象。我们可以通过 std::move 来实现
// 通过原始指针创建 taskPtr2 std::unique_ptr<Task> taskPtr2(new Task(55)); // 把taskPtr2中关联指针的所有权转移给 taskPtr4 std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2); // 现在taskPtr2关联的指针为空 if(taskPtr2 == nullptr) std::cout<<"taskPtr2 is empty"<<std::endl; // taskPtr2关联指针的所有权现在转移到了 taskPtr4中 if(taskPtr4 != nullptr) std::cout<<"taskPtr4 is not empty"<<std::endl; // 会输出55 std::cout<< taskPtr4->mId << std::endl;
std::move() 将把 taskPtr2 转换为一个右值引用。因此,调用 unique_ptr 的移动构造函数,并将关联的原始指针传输到 taskPtr4。在转移完原始指针的所有权后,taskPtr2将变为空。
- 释放关联原始指针
在 unique_ptr 对象上调用 release()将释放其关联的原始指针的所有权,并返回原始指针。这里是释放所有权,并没有delete原始指针,reset()会delete原始指针。std::unique_ptr<Task> taskPtr5(new Task(55)); // 不为空 if(taskPtr5 != nullptr) std::cout<<"taskPtr5 is not empty"<<std::endl; // 释放关联指针的所有权 Task * ptr = taskPtr5.release(); // 现在为空 if(taskPtr5 == nullptr) std::cout<<"taskPtr5 is empty"<<std::endl;
参考
引用csdn例程
原文地址:https://blog.csdn.net/xuelanga000/article/details/135990799
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_65861.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!