C++指针

C++ 指针具有和 C 指针的功能,但是随着 C++ 的发展, C++ 引入了更多新特性
指针常量
在C++11之前都会使用NULL表示空指针,有时使用NULL定义为常量0会导致程序在运行时出现意外的结果。分析下面的重载函数

#include 

using namespace std;

void pfun(char *str) {cout<<"char* version"<

编译无法通过:

[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 
#include 

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常用方法
    1. 创建unique_ptr对象

      std::unique_ptr<int> ptr1; //创建一个空的unique_ptr对象,因为没有与之关联的原始指针,所以它是空的 std::unique_ptr taskPtr(new Task(22)); //创建非空的 unique_ptr 对象,需要在创建对象时在其构造函数中传递原始指针 
      std::unique_ptr<Task> taskPtr(new std::unique_ptr<Task>::element_type(23)); //创建非空的 unique_ptr 对象 std::unique_ptr taskPtr2 = new Task(); // 编译错误,不能使用赋值的方法创建 
      std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34); //使用 std::make_unique 创建 unique_ptr 对象 / C++14 
      
    2. 判空

       // 方法1 
      if(!ptr1) 	std::cout<<"ptr1 is empty"<<std::endl; 
      
      // 方法2
      if(ptr1 == nullptr) 	std::cout<<"ptr1 is empty"<<std::endl;
      
    3. 获取被管理对象指针

      Task *p1 = taskPtr.get();//使用get() 函数获取管理对象的指针
      
    4. 重置 unique_ptr 对象

       taskPtr.reset(); //在 unique_ptr 对象上调用reset()函数将重置它,即它将释放delete关联的原始指针并使unique_
      
  1. 转移 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​​​​将变为空。
  2. 释放关联原始指针
    在 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例程

你可能感兴趣的:(C/C++学习,c++,开发语言)