C++智能指针详解(共享指针,唯一指针,自动指针)

前言:智能指针在C++11中引入,分为三类:

  1. shared_ptr:共享指针
  2. unique_ptr:唯一指针
  3. auto_ptr:自动指针

一、共享指针

几个共享指针可以指向同一个对象;
每当shared_ptr的最后一个所有者被销毁时,关联对象或关联资源就会被删除

1. 创建方式:
shared_ptr<string> pPoint{new string("pointer")};
shared_ptr<string> pPoint;
pPoint.reset(new string("point"));
shared_ptr<string> pPoint = make_shared<string>("point");
2. 使用:

UNIT1:引用

shared_ptr<string> pPoint1 = make_shared<string>("point1");
shared_ptr<string> pPoint2 = make_shared<string>("point2");

// *point:解引用;
(*pPoint1)[0] = 'P'; //point1 -> Point1

// point->API:解引用并调用其成员函数;
pPoint2->replace(0, 1, 'P'); //point2 -> Point2

// 向容器中插入point,增加一次引用
vector< shared_ptr<string>> vtPoint;
vtPoint.push_back(pPoint1);
vtPoint.push_back(pPoint2);
vtPoint.push_back(pPoint1); //这里*pPoint1虽然被插入了两遍,但是指向的还是同一个对象
*pPoint1 = "pPoint3"; //此时vtPoint中为pPoint3,pPoint2,pPoint3

这里智能指针用到的是一种引用技术:当一个对象被引用了多次时,那么这个对象的引用基数就会变大;当销毁引用这个对象的智能指针时,这个对象的引用基数就会变小;当引用基数变为0时,那么这个被引用对象就会被销毁,不会产生内存泄露的风险

UNIT2:比较运算符

shared_ptr<int> point1 = make_shared<int>(1);
shared_ptr<int> point2 = make_shared<int>(2);
shared_ptr<int> point3;
shared_ptr<double> point4 = make_shared<double>(1);

bool p1 = point3 == point3;
bool p2 = point1 < point2;
bool p3 = point1 > point3;
bool p4 = point1 == point4; //compile error

同类型的共享指针才能使用共享指针

UNIT3:强制类型转换

share_ptr<void> point(new int(1)); //共享指针内部保存void型指针
share_ptr<int> point(static_cast<int *>(point.get())); //compile error,undefined pointer
static_pointer_cast<int *>(point);

共享指针强制转换运算符允许将其中包含的指针强制转换为其他类型指针;
只能使用智能指针特定的强制转换运算符:

  • static_pointer_cast
  • dynamic_pointer_cast
  • const_pointer_cast

UNIT4:线程安全
共享指针不是线程安全的;
C++标准库提供了针对共享指针的原子接口;
针对共享指针本身的操作是原子的,并不包含该指针引用的具体值

atomic_is_lock_free(&point) //如果point的原子接口是没上锁的,那么返回true
atomic_load(&point) //返回point
atomic_store(&point1, point2) //使用point2对point进行赋值
atomic_exchange(&point1, point2) //交换point1和point2的值

注:
多个共享指针不能拥有同一对象,否则会出现段错误
可使用enable_shared_from_this和share_from_this生成共享指针

3. 销毁:

定义删除器,例:point(…,D)
删除器可以是普通函数、匿名函数、函数指针等符合签名要求的可调用的对象
只有最后引用对象的共享指针销毁时才会销毁对象

void delFunc(string *p)
{    
    cout << "Func del " << *p << endl;
    delete p;
}
cout << "begin" << endl;
shared_ptr<string> pPoint;
{
    shared_ptr<string> pPoint1(new string("point1"),
                        [](string* p) {
                            cout <<delete " << *p << endl;
                            delete p;
                        });
    pPoint1 = pPoint2;
    shared_ptr<string> pPoint2(new string("point2"), delFunc);
}
cout << "end" << endl;

注:
不能为数组创建一个共享指针
共享指针提供的默认删除程序将调用delete,而不是delete []
可使用自定义的删除器,删除器中使用delete[]
可使用default_delete作删除器,因为它使用delete []

附:弱指针(weak_ptr)
  • 弱指针是共享指针辅助类,其允许共享但不拥有对象,因此不会增加关联对象的引用次数
  • 不能使用运算符*和->直接访问弱指针的引用对象,而是使用lock函数生成关联对象的共享指针(可能为空)
  • 当拥有该对象的最后一个共享指针失去其所有权时,任何弱指针都会自动变为空

二、唯一指针

指针唯一性;
继承了自动指针auto_ptr,更不易出错;
抛出异常时可最大限度避免资源泄漏

1. 手动释放资源存在的问题:

问题代码示例:

void func1()
{
    ClassA* ptr = new ClassA; //Create an objects manually
    ... //Perform some operations
    //delete ptr; //Cleanup(Manually destroy objects)
}
void func2()
{
    ClassA* ptr = new ClassA; //Create an objects manually
    try {
        ... //Perform some operations
    } catch (...) { //Handle exception
        delete ptr; //Cleanup
        throw; //Rethrow the exception
    }
    delete ptr; //Clean up on normal exit
}

问题综述:
func1中,忘记释放资源导致资源泄露;
func2中,在释放资源如果发生异常导致资源泄露;
func2中,使用异常捕获的方法会随着资源数量和异常类型的增加导致代码变得复杂

唯一指针代码示例:

void func()
{
    //Create and initialize a unique_ptr pointer
    unique<ClassA> ptr(new ClassA);
    ... //Perform some operations
}

唯一指针可以解决func1和func2函数中资源释放的问题。

2. 使用
//创建唯一指针
unique_ptr<string> uq(new string("Point"));
(*uq)[0] = 'A'; //替换第一个字符
uq->append("one"); //追加字符串
cout << *uq << endl; //打印uq字符串
//空的唯一指针
unique_ptr<string> uq;
uq = nullptr;
uq.reset();
unique_ptr<string> uq(new string("Point"));
string* str = uq.release(); //uq释放,失去唯一性

注:
唯一指针定义了*、->运算符,没有定义类似++的指针算法
唯一指针不可使用赋值语法进行初始化,应使用普通指针初始化
唯一指针可以为空
release()可以让唯一指针返回其拥有的对象,并失去指向该对象的唯一性,调用release()的指针将指向返回的对象

3. 检查唯一指针是否拥有对象的三种方法:
//调用操作符bool()
if (uq) //如果uq不为空
{ 
    cout << *uq << endl;
}
//与nullptr进行比较
if (uq != nullptr) //如果uq不为空
//check unique_ptr中的原始指针是否为空
if (uq.get() != nullptr) //如果uq不为空
4. 指针对象唯一性的转移
//形参uq获得对象的所有权
void sink(unique_ptr<ClassA> uq) 
{
    ...
}
unique_ptr<ClassA> uq(new ClassA);
sink(move(uq)); //uq失去关联对象的所有权

unique_ptr<ClassA> source()
{
    unique_ptr<ClassA> uq(new ClassA);
    ...
    return uq; //将uq关联对象的所有权转移给调用函数
}
uq = source();

要将新值赋给唯一指针,该新值必须是唯一指针
函数可以作为数据的接收端,也可以作为数据发送源
return语句不需要move()的原因是C++11规定编译器将自动尝试移动

5.使用唯一指针处理数组
unique_ptr<string[]> uq(new string[666]); 
cout << uq[0] << endl;

针对数组的接口不提供运算符*->,而提供运算符[]
针对数组的销毁提供了特殊处理
若唯一指针失去对象所有权,则对其拥有的对象调用delete,而不是delete[]

6.使用唯一指针销毁资源
class ClassA {};

class ClassADeleter
{
public:
    void operator () (ClassA* obj) {
        cout << "call ClassA object's Deleter" << endl;
        delete obj;
    }
};

int main()
{
    unique_ptr<ClassA, ClassADeleter> up(new ClassA());
    return 0;
}

唯一指针引用的对象在销毁时需要进行除delete或delete []之外的其它操作时,必须自定义删除器
定义删除器的方法是必须将删除器的类型指定为第二个模板参数
删除器类型可以是函数、函数指针或函数对象

unique_ptr<int, void(*)(int*)> 
uq(new int[666],
	[](int* pointer) {
	...
	delete[] pointer;
	});

unique_ptr<int, function<void(int*)>> 
uq(new int[666],
	[](int* pointer) {
	...
	delete[] pointer;
	});

auto T = [](int* pointer) {
    ...
    delete[] p;
};
unique_ptr<int, decltype(l)>> uq(new int[666], T);

销毁其它类型资源时,需要指定函数或lambda表达式,必须将删除程序的类型声明为void(*)(T *)或 function <void (T *)>或使用decltype

三、自动指针

C++98中存在,于C++11中使用唯一指针替换其它

四、智能指针接口汇总

API NameFunc Def
weak_ptr wp默认构造函数;创建一个空的弱指针
weak_ptr wp(wp2)创建一个弱指针,共享由wp2拥有的指针的所有权
wp.~weak_ptr()析构函数;销毁弱指针,但对拥有的对象无效
wp = wp2赋值(wp之后共享wp2的所有权,放弃先前拥有的对象的所有权)
wp = sp用共享指针sp进行赋值(wp之后共享sp的所有权,放弃先前拥有的对象的所有权)
wp.swap(wp2)交换wp和wp2的指针
swap(wp1,wp2)交换wp1和wp2的指针
wp.reset()放弃拥有对象的所有权(如果有的话),并重新初始化为空的弱指针
wp.use_count()返回共享所有者的数量(拥有对象的shared_ptr数目);如果弱指针为空,则返回0
wp.expired()返回wp是否为空(等同于wp.use_count() == 0,但可能更快)
wp.lock()返回共享指针,该共享指针共享弱指针拥有的指针所有权(如果没有共享指针,则为空共享指针)
wp.owner_before(wp2)提供严格的弱排序和另一个弱指针
wp.owner_before(sp)通过共享指针提供严格的弱排序
shared_ptr sp(ptr,del)使用del作为删除器创建拥有*ptr的共享指针
shared_ptr sp(ptr, del, ac)使用del作为删除器并使用ac作为分配器创建一个拥有*ptr的共享指针
shared_ptr sp(nullptr)使用默认删除器(调用delete)创建空的共享指针
shared_ptr sp(nullptr, del)使用del作为删除器创建一个空的共享指针
shared_ptr sp(nullptr, del, ac)使用del作为删除器和ac作为分配器创建一个空的共享指针
shared_ptr sp(sp2)创建与sp2共享所有权的共享指针
shared_ptr sp(move(sp2))创建一个共享指针,该共享指针拥有先前由sp2拥有的指针(sp2之后为空)
shared_ptr sp(sp2, ptr)别名构造函数;创建一个共享指针,共享sp2的所有权,但引用*ptr
shared_ptr sp(wp)从弱指针wp创建共享指针
shared_ptr sp(move(up))从unique_ptr创建共享指针
shared_ptr sp(move(ap))从auto_ptr创建共享指针
sp.~shared_ptr()析构函数;如果sp拥有对象,则调用deleter
sp = sp2赋值(sp之后与sp2共享所有权,放弃先前拥有的对象的所有权)
sp = move(sp2)移动赋值(sp2将所有权转移到sp)
sp = move(up)使用unique_ptr进行移动赋值(up将所有权转让给sp)
sp = move(ap)使用auto_ptr进行移动赋值(ap将所有权转让给sp)
sp1.swap(sp2)交换sp1和sp2的指针和删除器
swap(sp1, sp2)交换sp1和sp2的指针和删除器
sp.reset()放弃所有权并将共享指针重新初始化为空
sp.reset(ptr)放弃所有权并使用默认删除器(称为delete)重新初始化共享指针,拥有*ptr
sp.reset(ptr, del)放弃所有权并使用del作为删除器重新初始化共享指针,拥有* ptr
sp.reset(ptr, del, ac)放弃所有权并重新初始化共享指针,拥有* ptr,使用del作为删除器,使用ac作为分配器
make_shared(…)为通过传递的参数初始化的新对象创建共享指针
allocate_shared(ac, …)使用分配器ac为由传递的参数初始化的新对象创建共享指针
sp.get()返回存储的指针(通常是拥有对象的地址,如果没有则返回nullptr)
*sp返回拥有的对象(如果没有则为未定义的行为)
sp->…提供对拥有对象的成员访问权限(如果没有,则行为未定义)
sp.use_count()返回共享所有者(包括sp)的数目;如果共享指针为空,则返回0
sp.unique()返回sp是否是唯一所有者(等效于sp.use_count()== 1,但可能更快)
static_pointer_cast(sp)sp的static_cast<>语义
dynamic_pointer_cast(sp)sp的dynamic_cast<>语义
const_pointer_cast(sp)sp的const_cast<>语义
get_deleter(sp)返回删除器的地址(如果有),否则返回nullptr
strm << sp调用原始指针的输出运算符(等于strm << sp.get())
sp.owner_before(sp2)提供严格的弱排序和另一个共享指针
sp.owner_before(wp)通过弱指针提供严格的弱排序
unique_ptr<…> up默认构造函数;使用默认/传递的删除器类型的实例作为删除器,创建一个空的唯一指针
unique_ptr up(nullptr)使用默认/传递的删除器类型的实例作为删除器,创建一个空的唯一指针
unique_ptr<…> up(ptr)使用默认/传递的删除器类型的实例作为删除器,创建拥有* ptr的唯一指针
unique_ptr<…> up(ptr,del)使用del作为删除器创建拥有* ptr的唯一指针
unique_ptr up(move(up2))创建一个拥有up2先前拥有的指针的唯一指针(此后up2为空)
unique_ptr up(move(ap))创建一个拥有先前由auto_ptr ap拥有的指针的唯一指针(此后ap为空)
up.~unique_ptr()析构函数;调用拥有者对象的删除器
up = move(up2)移动赋值(up2将所有权转移到up)
up = nullptr调用拥有者对象的删除器,并使其为空(等同于up.reset())
up1.swap(up2)交换up1和up2的指针和删除器
swap(up1,up2)交换up1和up2的指针和删除器
up.reset()调用拥有者对象的删除器,并使其为空(相当于up = nullptr)
up.reset(ptr)调用拥有者对象的删除器,并将共享指针重新初始化为自己的* ptr
up.release()将所有权放弃给调用者(不调用删除器就返回拥有的对象)
up.get()返回存储的指针(拥有的对象的地址;如果没有,则返回nullptr)
*up仅单个对象;返回拥有的对象(如果没有,则为未定义的行为)
up->…仅单个对象;提供拥有对象的成员访问权限(如果没有,则为未定义的行为)
up[idx]仅数组对象;返回具有存储数组的索引idx的元素(如果没有,则为未定义的行为)
up.get_deleter()返回删除器的引用
  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 共享内存是一种进程间通信方式,它使得在内存中创建一段可以被多个进程共享并访问的内存空间。这可以提高进程间的通信效率,因为在共享内存中的数据不需要使用复杂的消息传递机制来传递。 在C语言中,通过使用共享内存,可以非常方便地在不同的进程之间共享数据,而避免使用其他进程间通信方式(如管道、消息队列等)。要使用共享内存,在C语言中可以使用system V或POSIX标准的共享内存函数库,如shmget、shmctl等函数来进行共享内存的创建和管理。例如,可以使用shmget函数来创建共享内存区域,然后使用shmat函数将其映射到进程的地址空间中,实现对共享内存的访问。 需要注意的是,在使用共享内存时,需要保证进程之间对共享内存的读写操作的互斥性,以避免数据的不一致性和冲突。因此,在使用共享内存时,通常需要使用信号量、互斥锁等机制来对共享内存进行同步和保护。 总的来说,共享内存在C语言中的应用广泛,可以用来实现进程之间高效、快速的数据共享,同时也需要一定的技术和经验来有效地使用和管理。 ### 回答2: 共享内存是一种进程间通信的方式,它可以让多个进程之间快速、高效地共享数据。在共享内存中,几个进程可以通过一个共享的内存区域来访问同一组数据。这种方式通常比其他的进程间通信方式,比如管道、消息队列等更快速、更高效。 在C语言中,我们可以使用系统提供的共享内存函数来创建、读取、写入、删除共享内存。在创建共享内存时,我们需要指定共享内存的大小,并且需要给共享内存分配一个标识符,这个标识符可以让多个进程之间访问同一个共享内存。 在读写共享内存时,我们需要先将共享内存映射到当前进程的空间中,然后就可以通过指针访问共享内存中的数据了。在访问完共享内存后,我们需要将共享内存从当前进程的空间中解除映射,以免出现内存泄漏等问题。 总之,共享内存是一种非常有用的进程间通信方式,它可以帮助多个进程高效地共享数据。因此,我们在编写多进程程序时,可以尝试使用共享内存来提高程序的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gabriel.Tian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值