目录
前言
开发过程中,遇到了很多使用裸指针的情况,不经意间就容易出现内存泄漏的情况,由此想到了智能指针,借着这个机会,想说如何使用智能指针替换掉一些原始指针的情况(主要还是后续开发都使用智能指针,开闭原则)。
智能指针介绍
智能指针个人觉得就是用面向对象的方式,管理裸指针,离开智能指针对象的作用域也就结束指针生命周期。需要注意的是
- unique_ptr:独占型,无法使用unique_ptr进行相应的拷贝以及赋值,可以使用std::move(),将对象转移到另外的智能指针对象。
- share_ptr:共享型,可以使用share_ptr进行拷贝以及赋值,使用引用计数的方式管理指针,多线程安全,但是会出现循环引用的问题。
- weak_ptr:不参与自动内存管理,观察对应的share_ptr,解决循环引用问题,无法直接使用。使用lock()获取share_ptr,离开作用域,对应的share_ptr计数减少。
使用场景
1.unique_ptr
个人的理解unique_ptr是用于处理一种独特的指针对象,比如说商场出售了很多的饮料(此时是共享的),但是当我付款之后,我买的这瓶饮料就是我独占的了,此时,这瓶饮料就可以作为我这个对象的独占成员对象。此时的独占对象我就觉得可以是unique_ptr。这瓶饮料的成分是独特的,且是我自己的,不归其他人所有。
-
基础使用
代码内容:
创建unique_ptr<T>对象、C++14 make_unique、操作权转移、创建unique_ptr<T[]>数组对象、C++17 make_unique创建数组对象。
unique_ptr<int> up1(new int(10)); //存储在栈上
// unique_ptr<int> up4 = make_unique<int>(10); //C++ 14才允许有make_unique,但不支持动态数组。存储在堆上,但会自动释放
cout << *up1 << endl;
*up1 = 15;
cout << *up1 << endl;
unique_ptr<int> up2(up1.release()); //释放资源,转移操作权
// unique_ptr<int> up2(new int(15)); //如果提前初始化,std::move会先释放原来的指针,move之前会调用析构函数,可以试着把int改成析构函数输出的语句,会调用析构函数
// unique_ptr<int> up2 = std::move(up1); //移动语义,转移操作权
cout << *up2 << endl;
if(up1 == nullptr)
{
cout << "up1 is nullptr" << endl;
}
else
{
cout << "up1 is pointer" << endl;
}
unique_ptr<int[]> up3(new int[10]);
up3[0] = 7;
cout << up3[0] << endl;
// unique_ptr<int[]> up5 = make_unique<int[]>(5); //C++ 17,动态创建5个对象的数组
运行结果:
-
工厂模式
代码内容:定义饮料接口类、水类、茶类以及超市工厂、使用基类指针调用虚函数完成多态功能。
class Drink
{
public:
virtual ~Drink(){}
virtual void info() = 0;
};
class Water : public Drink
{
public:
virtual void info() override
{
cout << "water" << endl;
}
virtual void ~Water(){}
};
class Tea : public Drink
{
public:
virtual void info() override
{
cout << "tea" << endl;
}
virtual void ~Tea(){}
};
class shop
{
public:
static unique_ptr<Drink> creatWater() //返回基类指针
{
return unique_ptr<Drink>(new Water);
}
static unique_ptr<Drink> creatTea() //返回基类指针
{
return unique_ptr<Drink> (new Tea);
}
};
int main()
{
unique_ptr<Drink> dk1 = shop::creatWater();
unique_ptr<Drink> dk2 = shop::creatTea();
dk1->info();
dk2->info();
return 0;
}
2.shared_ptr
个人的理解share_ptr是一种共享的指针对象,比如说三兄弟共享同一个母亲以及同一个父亲,享受父母的保护。但是母亲、父亲共享三兄弟,此时父母组合了三兄弟的指针,三兄弟共享父母,此时三兄弟组合了父母的指针,引起了循环引用,当所有对象离开作用域时无法正常删除。
-
基础使用
代码内容:shared_ptr的初始化、赋值、拷贝、reset()、use_count()
shared_ptr<int> sp1(new int(7));
shared_ptr<int> sp2 = sp1;
shared_ptr<int> sp3(sp1);
cout << *sp2 << endl;
cout << *sp3 << endl;
sp1.reset(); //智能指针的引用计数减1,如果为0则释放资源
if(!sp1)
{
cout << "nullptr" << endl;
}
*sp2 = 6;
cout << *sp3 << endl; //sp2 sp3 依旧共享对象
cout << sp3.use_count() << endl; //获取当前shared_ptr的引用计数,不包括weak_ptr
运行结果:
-
单例模式
class Drink
{
public:
virtual ~Drink(){}
virtual void info() = 0;
static shared_ptr<Drink> instance();
};
class Water : public Drink
{
public:
virtual ~Water(){}
protected:
virtual void info() {cout << "water" << endl;}
};
shared_ptr<Drink> Drink::instance()
{
static shared_ptr<Drink> entity(new Water());
return entity;
}
int main()
{
shared_ptr<Drink> entity = Drink::instance(); //获取基类对象指针
entity->info();
return 0;
}
-
循环引用情况
代码内容:人类基类、父亲类、母亲类、儿子类、初始化父亲母亲以及三个儿子类、各个对象行为、释放调用析构函数。(循环引用修改human对象weak_ptr为shared_ptr即可)
class human
{
public:
virtual ~human(){}
virtual void behavior() = 0;
virtual void init(const string& name) {mName = name;}
virtual string name(){return mName;}
virtual void setFamily(shared_ptr<human> hm) {mFamily.push_back(hm);}
protected:
string mName;
vector<weak_ptr<human>> mFamily; //循环引用将该weak_ptr替换成share_ptr,将无法正常析构
};
class father : public human
{
public:
virtual ~father(){cout << mName.c_str() << "~father" << endl;}
virtual void behavior() override
{
cout << "I need to protected my family!" << endl;
}
};
class mother : public human
{
public:
virtual ~mother(){cout << mName.c_str() << "~mother" << endl;}
virtual void behavior() override
{
cout << "cook and take care of my family!" << endl;
}
};
class child : public human
{
public:
virtual ~child(){cout << mName.c_str() << " ~child " << endl;}
virtual void behavior() override
{
cout << "play game!" << endl;
}
};
int main()
{
shared_ptr<human> fa(new father);
shared_ptr<human> mo(new mother);
shared_ptr<human> bro1(new child);
shared_ptr<human> bro2(new child);
shared_ptr<human> bro3(new child);
fa->init("fa");
mo->init("mo");
bro1->init("bro1");
bro2->init("bro2");
bro3->init("bro3");
fa->setFamily(bro1);
fa->setFamily(bro2);
fa->setFamily(bro3);
mo->setFamily(bro1);
mo->setFamily(bro2);
mo->setFamily(bro3);
bro1->setFamily(fa);
bro1->setFamily(mo);
bro2->setFamily(fa);
bro2->setFamily(mo);
bro3->setFamily(fa);
bro3->setFamily(mo);
fa->behavior();
mo->behavior();
bro1->behavior();
bro2->behavior();
bro3->behavior();
return 0;
}
运行结果:
3.weak_ptr
-
基础使用
weak_ptr是share_ptr的配套使用,是为了避免循环引用出现的。
代码内容:生成weak_ptr对象、weak_ptr的访问方式
shared_ptr<int> sp(new int(7));
weak_ptr<int> wp(sp);
{
cout << sp.use_count() << endl;
shared_ptr<int> sp1 = wp.lock();
cout << *sp1 << endl;
cout << sp.use_count() << endl;
}
cout << sp.use_count() << endl;
运行结果:
结语
以上就是本人在工作中对于遇到的一些指针使用场景,需要修改成智能指针及相关功能的一些实际操作。代码内容都是自己实际编写的,关于智能指针的概念则主要通过chatGPT获取。建议大家还是根据需要学习以及实操(正所谓好记性不如烂笔头)。