C++进阶(语法篇)—第10章 智能指针

本文详细介绍了C++中的智能指针,包括unique_ptr、shared_ptr和weak_ptr的使用,阐述了它们如何解决内存泄漏问题以及处理多个指针指向同一对象的场景。通过示例代码解释了unique_ptr的自动析构功能,以及shared_ptr和weak_ptr在处理交叉引用时的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

10.智能指针

10.1 unique_ptr

先看下面的代码:

#include <iostream>

 

using namespace std;

void Smart_pointer(int a);

int main(int argc, char ** argv)

{

Smart_pointer(1);

 

return 0;

}

void Smart_pointer(int a)

{

int * iptr = new int(a);

cout << "*iptr = " << *iptr << endl;

return;

}

当Smart_pointer函数执行完后,由于没有delete iptr(忘了写),因此iptr被销毁,而iptr指向的那块堆内存依旧存在,造成了内存泄漏。

注:当main函数执行完后,系统会自动清理堆空间;但是有的程序main函数会一直执行。

#include <iostream>

 

using namespace std;

void Smart_pointer(int a);

int main(int argc, char ** argv)

{

Smart_pointer(1);

 

return 0;

}

void Smart_pointer(int a)

{

int * iptr = new int(a);

cout << "*iptr = " << *iptr << endl;

//...发生异常...

delete iptr;

return;

}

当Smart_pointer函数未执行到delete处发生了异常,导致未释放堆空间造成内存泄漏。

注:在异常那一章讲解了这个问题,可以再catch和throw一次;但是不能每个函数都这么处理,这样太耗费资源。

#include <iostream>

 

using namespace std;

 

class SmartPointer {

private:

int * sp;

public:

SmartPointer():sp(NULL){}

SmartPointer(int * sp)

{

cout << "SmartPointer(int * sp)" << endl;

this->sp = sp;

}

~SmartPointer()

{

cout << "~SmartPointer()" << endl;

if (sp)

delete sp;

}

int operator*()

{

return *sp;

}

 

};

void Smart_pointer(int a);

int main(int argc, char ** argv)

{

Smart_pointer(1);

 

return 0;

}

void Smart_pointer(int a)

{

//int * iptr = new int(a);

//cout << "*iptr = " << *iptr << endl;

//delete iptr;

SmartPointer sp = new int(a);

cout << "*sp = " << *sp << endl;

return;

}

运行结果:

SmartPointer(int * sp)

*sp = 1

~SmartPointer()

执行了析构函数,使得sp被delete,即堆内存被释放。

为什么加上类SmartPointer 就可以了呢?

当执行SmartPointer sp = new int(a),实际分为2步:int * iptr = new int(a)和SmartPointer sp = iptr。当函数Smart_pointer执行完后,会销毁SmartPointer类的对象sp,调用sp类的析构函数,因此可以在析构函数中delete,这样会没有了内存泄漏问题。

其实在C++11的库中,提供一个智能指针模板unique_ptr,这样就不需要我们自己来提供智能指针。它需要包含头文件memory(被头文件iostream包含了)和使用名称空间std。

#include <iostream>

 

using namespace std;

 

void Smart_pointer(int a);

int main(int argc, char ** argv)

{

Smart_pointer(1);

 

return 0;

}

void Smart_pointer(int a)

{

unique_ptr<int> up_i(new int(a));

cout << " *up_i  = " << *up_i << endl;

//不需要delete

return;

}

运行结果:

 *up_i  = 1

智能指针模板unique_ptr只能处理一个指针指向同一个对象的问题。如:

unique_ptr<int> up_i1(new int(a));

unique_ptr<int> up_i2;

up_i2 = up_i1;//编译出错

那么当多个指针指向同一个对象时,该怎么办呢?

10.2 shared_ptr

#include <iostream>

 

using namespace std;

class RefBase {

private:

int count;

public:

RefBase():count(0) {}

void incStrong() { count++; }

void decStrong() { count--; }

int getCount() { return count; }

};

template<typename T>

class SmartPointer {

private:

T * sp;

public:

SmartPointer() :sp(NULL) {}

SmartPointer(T * sp)

{

cout << "SmartPointer(T * sp)" << endl;

this->sp = sp;

sp->incStrong();

}

SmartPointer(const SmartPointer & c_sp)

{

cout << "SmartPointer(const SmartPointer & c_sp)" << endl;

this->sp = c_sp.sp;

this->sp->incStrong();

}

~SmartPointer()

{

cout << "~SmartPointer()" << endl;

if (sp)

{

cout <<"count = "<< sp->getCount() << endl;

sp->decStrong();

if(sp->getCount() == 0)

delete sp;

}

}

T operator*()

{

cout << "T operator*()" << endl;

return *sp;

}

 

};

 

class Student:public RefBase

{

private:

const char * name;

int age;

public:

Student() :name("no name"), age(0) { cout << "Student()" << endl; }

Student(const char * name, int age)

{

cout << "Student(const char * name, int gae)" << endl;

this->name = new char[strlen(name) + 1];

strcpy_s(const_cast<char *>(this->name), strlen(name) + 1, name);

this->age = age;

}

~Student()

{

if (this->name)

{

if (this->getCount() == 0)

{

cout << "~Student()" << endl;

delete this->name;

}

}

}

void pintf(void)

{

cout << "name: " << this->name << endl << "age:" << this->age << endl;

}

};

 

void test_sp(SmartPointer<Student>& a);

int main(int argc, char ** argv)

{

SmartPointer<Student> sp_stu = new Student("zhangsan",16);

test_sp(sp_stu);

 

return 0;

}

void test_sp(SmartPointer<Student>& a)

{

SmartPointer<Student> sp = a;

(*sp).pintf();

return;

}

运行结果:

Student(const char * name, int gae)

SmartPointer(T * sp)

SmartPointer(const SmartPointer & c_sp)

T operator*()

name: zhangsan

age:16

~SmartPointer()

count = 2

~SmartPointer()

count = 1

~Student()

在main函数中使用new创建的对象,有main()中的sp_stu和test_sp()中的sp指向它,共2个指针指向同一个对象。

我使用了类模板对上一个类SmartPointer进行了改进,使它更具有重用性。创建了基类RefBase进行引用计数,每引用一次count加1,析构一次count减1,当count为0时delete。

问题1:为什么拷贝构造函数中对count加1,加的却是原来Student对象的count?

因为这里的拷贝构造函数,仅仅是地址的复制,即是浅拷贝而不是深拷贝,它指向的还是原来的Student对象。

问题2:为什么要把引用计数放到RefBase中,而不是类SmartPointer中?

因为对于每一个对象都要有一个引用计数,它最好和实际对象绑定。若放在SmartPointer中,当test_sp执行完后,由于临时变量SmartPointer<Student> sp的销毁,导致调用Student的析构函数,delete掉数据成员name,最后运行失败。因此在Student的析构函数中增加对count的判断。

问题3:难道我们要自己提供和维护这样的RefBase类和SmartPointer类吗?有没有别的办法?

使用C++11库中的shared_ptr。它需要包含头文件memory(被头文件iostream包含了)和使用名称空间std。

#include <iostream>

 

using namespace std;

class RefBase {

private:

int count;

public:

RefBase():count(0) {}

void incStrong() { count++; }

void decStrong() { count--; }

int getCount() { return count; }

};

template<typename T>

class SmartPointer {

private:

T * sp;

public:

SmartPointer() :sp(NULL) {}

SmartPointer(T * sp)

{

cout << "SmartPointer(T * sp)" << endl;

this->sp = sp;

sp->incStrong();

}

SmartPointer(const SmartPointer & c_sp)

{

cout << "SmartPointer(const SmartPointer & c_sp)" << endl;

this->sp = c_sp.sp;

this->sp->incStrong();

}

~SmartPointer()

{

cout << "~SmartPointer()" << endl;

if (sp)

{

cout <<"count = "<< sp->getCount() << endl;

sp->decStrong();

if(sp->getCount() == 0)

delete sp;

}

}

T operator*()

{

cout << "T operator*()" << endl;

return *sp;

}

 

};

 

class Student:public RefBase

{

private:

const char * name;

int age;

public:

Student() :name("no name"), age(0) { cout << "Student()" << endl; }

Student(const char * name, int age)

{

cout << "Student(const char * name, int gae)" << endl;

this->name = new char[strlen(name) + 1];

strcpy_s(const_cast<char *>(this->name), strlen(name) + 1, name);

this->age = age;

}

~Student()

{

if (this->name)

{

if (this->getCount() == 0)

{

cout << "~Student()" << endl;

delete this->name;

}

}

}

void pintf(void)

{

cout << "name: " << this->name << endl << "age:" << this->age << endl;

}

};

 

void test_sp(shared_ptr<Student>& a);

int main(int argc, char ** argv)

{

//SmartPointer<Student> sp_stu = new Student("zhangsan",16);

shared_ptr<Student> sp_stu(new Student("zhangsan", 16));

test_sp(sp_stu);

 

return 0;

}

void test_sp(shared_ptr<Student>& a)

{

shared_ptr<Student> sp = a;

(*sp).pintf();

return;

}

运行结果:

Student(const char * name, int gae)

name: zhangsan

age:16

~Student()

智能指针模板shared_ptr适用于多个指针指向同一个对象的情形。

10.3 weak_ptr

#include <iostream>

 

using namespace std;

 

class Son;

class Father;

 

class Father

{

public:

shared_ptr<Son> son;

~Father()

{

cout << "~Father() " << endl;

}

};

 

class Son

{

public:

shared_ptr<Father> father;

~Son()

{

cout << "~Son()"<< endl;

}

};

 

void CrossReference(void);

int main()

{

cout<<"start of main..."<<endl;

CrossReference();

cout << "end of main..." << endl;

return 0;

}

 

void CrossReference(void)

{

shared_ptr<Father> f(new Father);

shared_ptr<Son> s(new Son);

 

// 交叉引用

f->son = s;

s->father = f;

 

cout << "类Father的引用计数:" << f.use_count() << endl;

cout << "类Son的引用计数:" << s.use_count() << endl;

return;

}

运行结果:

start of main...

类Father的引用计数:2

类Son的引用计数:2

end of main...

为什么没有调用类Father和类Son的析构函数?

在这之前,我们先了解什么是交叉引用。类A中对类B进行引用,则A对象要销毁则必须先销毁B对象;类B中对类A进行引用,则B对象要销毁则必须先销毁A对象。这样就造成了一个死锁,谁都销毁不了。类A中对类B进行引用,对象A可以决定对象B的生死,称之为强指针或强引用;A中对类B进行引用,对象A决定不了对象B的生死,称之为弱指针或弱引用。

对于上面代码中的交叉引用,可以用弱指针来解决。在C++11库中提供了弱指针weak_ptr。它需要包含头文件memory(被头文件iostream包含了)和使用名称空间std。

#include <iostream>

 

using namespace std;

 

class Son;

class Father;

 

class Father

{

public:

weak_ptr<Son> son;

~Father()

{

cout << "~Father() " << endl;

}

};

 

class Son

{

public:

shared_ptr<Father> father;

~Son()

{

cout << "~Son()"<< endl;

}

};

 

void CrossReference(void);

int main()

{

cout<<"start of main..."<<endl;

CrossReference();

cout << "end of main..." << endl;

return 0;

}

 

void CrossReference(void)

{

shared_ptr<Father> f(new Father);

shared_ptr<Son> s(new Son);

 

// 交叉引用

f->son = s;

s->father = f;

 

cout << "类Father的引用计数:" << f.use_count() << endl;

cout << "类Son的引用计数:" << s.use_count() << endl;

return;

}

运行结果:

start of main...

类Father的引用计数:2

类Son的引用计数:1

~Son()

~Father()

end of main...

只要交叉引用的其中一边使用weak_ptr就可以解开死锁。

在安卓的源代码中,提供了轻量级指针、强指针、弱指针对C++的对象进行回收,有兴趣的可以研究下它的原理。

https://blog.youkuaiyun.com/kc58236582/article/details/52279346这篇博客写的不错,大家可以看看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值