智能指针(二)智能指针的几个问题

一、智能指针有多少种构造方法?

  • 默认构造。构造一个空的智能指针;
std::unique_ptr<int> ptr1;
std::shared_ptr<int> ptr1;
  • 原始指针构造。
std::unique_ptr<int> ptr2(new int(42));
std::shared_ptr<int> ptr2(new int(42));
  • 移动构造。利用另一个已经构造的智能指针进行构造;
std::unique_ptr<int> ptr3(std::move(ptr2));
std::shared_ptr<int> sptr2 = std::move(sptr1);
  • 拷贝构造。(声明为explicit
std::unique_ptr<int> uptr1 = std::make_unique<int>(42);
std::shared_ptr<int> sptr1 = std::make_shared<int>(42);
  • 带删除器构造函数声明

前面的例子模板参数只有一个,事实上为了增加智能指针的灵活性,我们还可以传递一个删除器。删除器可以是:函数、有operator()的类和Lambda表达式。

#include <iostream>
#include <memory>

void customDeleter(int* ptr) {
    std::cout << "Custom deleter (function) is deleting the pointer\n";
    delete ptr;
}

int main() {
    std::unique_ptr<int, void(*)(int*)> ptr(new int, customDeleter);
    *ptr = 42;
    std::cout << "Value: " << *ptr << "\n";
    return 0;
}

二、为什么拷贝构造要声明为隐式?

explicit关键字是用来修饰构造函数和转换运算符的,表示禁止隐式转换。转换运算符是一种特殊的成员函数,它允许一个类类型的对象被转换为另一种类型。基本格式是:operator 类型名() const;

查看下面代码:

void func(std::unique_ptr<MyClass> ptr) { /*...*/ }
MyClass* raw_ptr = new MyClass();
func(raw_ptr);

C++通过声明拷贝构造函数为隐式禁止了上述行为,编译阶段就会出错。假设我们允许这种行为,在原始指针raw_ptr发生隐式转换,std::unique_ptr将会接管这部分内存,程序运行完成后,因为引用计数变成了0,所以将会释放掉这部分内存。这样一来我们原始指针失效,尝试访问这个原始指针就会出现错误。

三、还有一些别的什么操作吗?

  • 赋值操作
sp1=sp2;
up1=up2;//deleted funciton,not ok
  • 解引用操作

*sp
*up

  • get()方法

p.get()返回一个与智能指针指向相同的内置指针

  • reset()方法

reset有多个重载形式,分别是p.reset() p.reset(q) p.reset(q,d),作用是将一个智能指针重置为内置空指针(或q指针)

  • ->mem操作

指针指针指向对象中的成员

  • 指针交换操作 swap(p,q) p.swap(q)

完成指针指向对象的交换

  • 隐式转换

定义了智能指针到bool的隐式转换,spup在作为条件时都会被隐式转换为bool

  • 共享指针的特殊成员

p.use_count() p指向对象对应的引用计数,可能很慢,主要用于调试
p.unique() 对象是否只有一个引用计数

五、make_shared是什么?为什么说它更加高效?

std::make_shared是C++11标准引入的一个用于创建std::shared_ptr实例的一个实用函数。再讲它为什么高效之前,我们先来看一个例子:

std::shared_ptr<MyClass> ptr(new MyClass(42)); 

它一共进行了几次内存分配?答:两次。

  • new MyClass(42) 使用运算符new在堆上分配了int大小的内存;
  • 因为智能指针需要额外信息,所以进行了第二次内存分配;

为了解决这个问题,C++11引入了std::make_shared:

std::shared_ptr<MyClass> ptr=std::make_shared<MyClass>(42);

make_shared 直接构造了智能指针所需要的内存,只需要一次内存分配。

所谓高效指的构造的时候只需要分配一次内存。

有时候我们也会听到make_shared更加安全,这又是为什么呢?同样地,我们也来看一个例子:

#include <memory>

class MyClass {
public:
    MyClass(int value) {
        if (value < 0) {
            throw std::runtime_error("Negative value");
        }
    }
};

int main() {
    try {
        std::shared_ptr<MyClass> ptr(new MyClass(-1));
    }
    catch (const std::exception& e) {
        // 在这里捕获异常
    }

    return 0;
}

假设MyClass抛出一个异常,还没轮到智能指针接管这部分内存,就已经捕获异常了,处理不当可能会造成内存泄漏。如果我们使用std::make_shared

int main() {
    try {
        std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(-1);
    }
    catch (const std::exception& e) {
        // 在这里捕获异常,没有内存泄漏
    }

    return 0;
}

这样一来,无论构造是否成功都能够成功的析构。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值