MENU

C++11新特性之智能指针

前言

C/C++不像Java那样JVM有垃圾回收机制,需要使用者mallocnew分配内存,freedelete释放内存。一旦忘记释放内存,则会出现内存泄露问题。
在C++11标准中新增unique_ptrshared_ptrweak_ptr这三个智能指针来自动回收堆分配的对象。C++98中的auto_ptr也置为deprecated。下面总结下这四个指针的区别。

auto_ptr

在C++98开始推出auto_ptr管理裸指针,让开发者不需要手动释放指针内存,当生命周期结束时自动释放内存。但是auto_ptr在转移所有权会出现野指针导致崩溃。

void autoPtr(){
    std::auto_ptr<int> auto_ptr1(new int (1));
    std::auto_ptr<int> auto_ptr2 = auto_ptr1;  // 转移控制权
    *auto_ptr1 += 1;   // 程序崩溃
    // 由于auto_ptr1转移给auto_ptr2,auto_ptr1变为空指针,导致程序崩溃,可以通过get方法先判空
    if (auto_ptr1.get()){ // 判空后执行
        // do somethings
    }
}

unique_ptr

C++11新出现unique_ptr智能指针管理内存,从命名上可以知道,该智能指针智能被唯一一个对
象持有
,与auto_ptr一样拥有唯一拥有控制权的特性,不同的是unique_ptr是没有复制构造函数的,只能通过显示std::move()方法转移控制权。

#include <memory>
int main(int argc, char *argv[]) {

    auto unique_ptr1 = std::unique_ptr<int>();

    auto unique_ptr2 = unique_ptr1;  // 直接复制,错误。编译出错

    auto unique_ptr3 = std::move(unique_ptr1); // unique_ptr1转移控制权到unique_ptr3,unique_ptr1变为nullptr

    return 0;
}

unique_ptr的应用场景

#include <memory>
#include <iostream>

class A {
public:
    A() {}

    ~A() {}
};

class B {
public:
    B() : a(new A()) {

    }

    void output() {
        std::cout << "这是B对象" << std::endl;
    };

    ~B() {
        // 不释放a
    }

private:
    A *a;
};

int main(int argc, char *argv[]) {
    // B析构函数中A对象不会释放,导致内存泄露
    B *b = new B();
    b->test();

    // 使用unique_ptr会自动释放内存
    auto unique_ptr1 = std::unique_ptr<B>(new B());
    unique_ptr1->output();

    return 0;
}

shared_ptr

相对比unique_ptrshared_ptr支持复制,即多个对象共享同一块内存。采用引用计数的方式式实现内存的自动管理,当复制一次值+1,释放一次则值-1,直到值为0内存会被回收。

#include <iostream>
#include <memory>

using namespace std;

int main() {

    // 推荐方式
    shared_ptr<int> sharedPtr1(new int(5)); // 匿名函数
    shared_ptr<int> sharedPtr2 = make_shared<int>(5); // 工厂模式

    // 引用计数
    std::shared_ptr<int> sharedPtr3 = sharedPtr1;
    cout << sharedPtr1.use_count() << std::endl;  // 输出为2
    sharedPtr3.reset();                           // 释放
    cout << sharedPtr1.use_count() << endl;       // 输出为1

    return 0;
}

引用计数的方式引起循环引用导致无法被正常的释放,如下示例。

#include <iostream>
#include <memory>

using namespace std;

class Child;

class Parent {
public:
    shared_ptr<Child> child;
};

class Child {
public:
    shared_ptr<Parent> parent;
};

int main() {

    shared_ptr<Parent> parentPtr = make_shared<Parent>();
    shared_ptr<Child> childPtr = make_shared<Child>();
    // 循环引用 导致内存永远不会被释放内存泄露
    parentPtr->child = childPtr;
    childPtr->parent = parentPtr;

    return 0;
}

weak_ptr

在上一part中介绍得share_ptr由于采用引用计数的方式,在循环引用时候时候use_count的值+1,导致释放后计数值依然为1。而weak_ptr则用于解决此问题,share_ptr赋值给weak_ptr,不会引起引用计数值+1,只是在弱引用计数值+1,因此不会影响到share_ptr的生命周期。

#include <iostream>
#include <memory>

using namespace std;

class Child;

class Parent {
public:
    shared_ptr<Child> child;
};

class Child {
public:
    weak_ptr<Parent> parent;  // 改为weak_ptr不会导致强引用计数值+1
};

int main() {

    shared_ptr<Parent> parentPtr = make_shared<Parent>();
    shared_ptr<Child> childPtr = make_shared<Child>();
    // 循环引用 导致内存永远不会被释放内存泄露
    parentPtr->child = childPtr;
    childPtr->parent = parentPtr;

    return 0;
}

参考文章
C++ 智能指针的正确使用方式
C++ 知识点 | C11 智能指针

返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码
添加新评论