头文件< memory >
智能指针注意事项
- 智能指针就是类,类中有一个成员管理着原始指针(他自己的地址和他管理的指针地址是两个东西)
- 智能指针的目的是解决资源释放问题
- 智能指针不支持指针运算:+,-,++,–
但是常规指针支持
独占指针 unique_str
独占指针独享它指向的对象
初始化
方法①
stu* s1 = new stu("Alice");//类new必须是指针
unique_ptr <stu> u1(s1);
方法②【常用】
unique_ptr<stu>u2(new stu("Bob"));
方法③【C++14标准】
unique_ptr<stu>u3 = make_unique<stu>("Cindy");
使用(相关函数)
u1->name;
(*u1).name;//解引用
赋值nullptr释放对象
赋值NULL后对象被释放,抛出异常
release 释放控制权
释放对原始指针的控制权,将unique_ptr置为空,返回裸指针
如下图(释放后仍调用会异常)
move转变原指针控制权
reset()释放对象
u1.reset();//释放u1指向的资源对象
u1.reset(nullptr);//释放u1指向的资源对象
u1.reset(new stu("Bob"));//释放u1指向的资源对象,并指向新的
unique_ptr赋值原则
将一个unique_ptr指针赋给另一个时遵循的原则:
右值如果会继续存在则不能赋值(如下不被允许)unique_ptr <stu> u1(new stu("alice")); unique_ptr <stu> u2; u2 = u1 ;//是错误的,因为u1会继续存在
右值时临时变量则可以赋值(如下被允许)
unique_ptr <stu> u2; u2 = fun(); unique_ptr <stu> fun(){ unique_ptr <stu>s1(new stu("alice")); return s1; }
多态 demo
#include <iostream>
#include <memory>
using namespace std;
class base {
public:
virtual void show1() = 0;//纯虚函数
virtual void show2() {}//虚函数
};
class son1 :public base{
public:
void show1();
void show2();
};
void son1::show1() {
cout << "我是孩子1号重写了base类show1函数" << endl;
}
void son1::show2() {
cout << "我是孩子1号我重写了base类show2函数" << endl;
}
class son2 :public base {
public:
void show1();
void show2();
};
void son2::show1() {
cout << "我是孩子2号重写了base类show1函数" << endl;
}
void son2::show2() {
cout << "我是孩子2号我重写了base类show2函数" << endl;
}
int main() {
int id;
while (1) {
cin >> id;
if (id == 1) {
unique_ptr <son1> u1(new son1);
u1->show1();
u1->show2();
}
else {
unique_ptr <son2> u1(new son2);
u1->show1();
u1->show2();
}
}
}
智能指针不是绝对安全的
如果在程序中调用exit()退出,全局unique_ptr可以自动释放,局部的无法释放
智能指针数组
常规类数组指针demo
用完必须手动释放
delete []arr;
#include <iostream>
#include <memory>
using namespace std;
class base {
public:
virtual void show1() = 0;
virtual void show2() {}
public:
string name;
};
class son1 :public base{
public:
void show1();
void show2();
};
void son1::show1() {
cout << "我是孩子1号重写了base类show1函数" << endl;
cout <<"我叫:" << name << endl;
}
void son1::show2() {
cout << "我是孩子1号我重写了base类show2函数" << endl;
}
class son2 :public base {
public:
void show1();
void show2();
};
void son2::show1() {
cout << "我是孩子2号重写了base类show1函数" << endl;
cout << "我叫:" << name << endl;
}
void son2::show2() {
cout << "我是孩子2号我重写了base类show2函数" << endl;
}
int main() {
son1* arr = new son1[2];//普通指针数组
arr[0].name = "Alice";
arr[1].name = "Bob";
arr[0].show1();
arr[0].show2();
arr[1].show1();
arr[1].show2();
delete[]arr;
}
智能数组指针demo
#include <iostream>
#include <memory>
using namespace std;
class base {
public:
virtual void show1() = 0;
virtual void show2() {}
public:
string name;
};
class son1 :public base{
public:
void show1();
void show2();
};
void son1::show1() {
cout << "我是孩子1号重写了base类show1函数" << endl;
cout <<"我叫:" << name << endl;
}
void son1::show2() {
cout << "我是孩子1号我重写了base类show2函数" << endl;
}
class son2 :public base {
public:
void show1();
void show2();
};
void son2::show1() {
cout << "我是孩子2号重写了base类show1函数" << endl;
cout << "我叫:" << name << endl;
}
void son2::show2() {
cout << "我是孩子2号我重写了base类show2函数" << endl;
}
int main() {
unique_ptr <son2[] > arr( new son2[2]);//普通指针数组
arr[0].name = "Alice";
arr[1].name = "Bob";
arr[0].show1();
arr[0].show2();
arr[1].show1();
arr[1].show2();
}
函数后加delete表示禁用该函数(c++11)
unique_ptr禁用了拷贝构造函数和赋值函数
unique_ptr(const unique_ptr &)=delete; unique_ptr& operator=(const unique_ptr&)=delete;
如下代码仅用了拷贝构造函数
stu(string name) = delete;
unique_ptr注意事项
智能指针是类,可以传地址,如
fun(&pu); void fun(unique_ptr<stu>*p){ cout<<(*p)->name; }
但是首推引用,不能值传递
fun(pu); void fun(unique_ptr<stu>&p){ cout<< p->name; }
共享指针 shared_ptr
- 多个共享指针可以指向相同的对象(同一个)
【资源没有被复制,只是被共享】- unique_ptr能解决问题不要用shared_ptr,前者更高效,占用资源更少
初始化
方法①
stu* s1 = new stu("Alice");//类new必须是指针
shared_ptr <stu> u1(s1);
方法②【常用】
shared_ptr<stu>u2(new stu("Bob"));
方法③【C++11标准】【推荐】
shared_ptr<stu>u3 = make_unique<stu>("Cindy");
方法④【注意格式】【无拷贝构造】
shared_ptr <stu> u1(new stu("Alice"));
shared_ptr <stu> u2(u1);//可以用括号
shared_ptr <stu> u3 = u1;//可以用等号
cout << u3.use_count() << endl;
使用(相关函数)
u1->name();
(*u1).name;//解引用
shared_ptr支持赋值[unique_ptr看情况]
shared_ptr <stu> u2(u1);//可以用括号
shared_ptr <stu> u3 = u1;//可以用等号
- 赋值后左值计数器-1,右值计数器+1
- 指向资源的指针多一个就+1,少一个就-1
- 计数器为0会释放资源
shared_ptr <stu> a1(new stu("Alice"));
shared_ptr <stu> a2(a1);//可以用括号
shared_ptr <stu> a3 = a1;//可以用等号
shared_ptr <stu> b1(new stu("Alice"));
shared_ptr <stu> b2(b1);//可以用括号
cout <<"a.count=" << a1.use_count() << endl;
cout <<"b.count=" << b1.use_count() << endl;
//a1赋给b1,相当于覆盖了一个b1,b1-1
b1 = a1;
cout << "a.count=" << a1.use_count() << endl;
cout << "b.count=" << b2.use_count() << endl;
b2 = b1;
cout << "a.count=" << a1.use_count() << endl;
cout << "b.count=" << b2.use_count() << endl;
计数函数 use_count()
unique() 1为True其他False
不为1为False(为2、3、4都不行)
必须是1
其他函数与unique_ptr类似不做演示
智能指针删除器
(缺省删除器)默认情况下,智能指针过期后用delete释放管理的资源,但是程序猿可以自定义删除器,改变释放资源的行为
【形参为原始指针】
unique_ptr删除器[decltype]
缺省|全局函数|lambda|仿函数
//缺省
unique_ptr <stu> a11(new stu("Alice"));
//普通函数
unique_ptr <stu,decltype(delet)*> a1(new stu("Alice"), delet);
//lambda
unique_ptr < stu, decltype(delet_lambda) > a2(new stu("Bob"), delet_lambda);//可以用括号
//仿函数
unique_ptr <stu,dele> a3(new stu("Carry"),dele());
a1.reset();
a2.reset();
a3.reset();
shared_ptr删除器件
缺省|全局函数|lambda|仿函数
#include <iostream>
#include <memory>
using namespace std;
class stu {
public:
stu(string name) : name(name) {}
string name;
};
class dele {
public:
void operator()(stu* a) {
cout << "仿函数,自定义删除器删除" << endl;
delete a;
}
};
void delet(stu* a) {
cout << "全局函数,自定义删除器删除" << endl;
delete a;
}
auto delet_lambda = [](stu *a) {
cout << "lambda函数,自定义删除器删除" << endl;
delete a;
};
int main() {
shared_ptr <stu> a11(new stu("Alice"));//缺省
shared_ptr <stu> a1(new stu("Alice"),delet);
shared_ptr <stu> a2(new stu("Bob"),delet_lambda);//可以用括号
shared_ptr <stu> a3(new stu("Carry"),dele());
a1.reset();
a2.reset();
a3.reset();
}
weak_ptr
为了解决循环引用导致技术永远无法归0,资源部会被释放的问题
weak_ptr是为了配合shared_ptr而引入的,它可以指向一个由shared_ptr管理的资源但不影响生命周期,即绑定之后weak_ptr不影响shared_ptr的引用周期,但是他知道对象是否还活着
-【weak_ptr更像shared_ptr的助手,解决它无法处理的循环问题】
shared_ptr出现的问题
【引用计数器失灵,双方都在循环等待对方释放】
#include <iostream>
#include <memory>
using namespace std;
class tec;//要先声明tec,不然stu在声明tec时会未定义
class stu {
public:
string name;
stu(string name) : name(name) {
cout << "我是stu的有参构造函数,我是" << name << endl;
}
~stu() {
cout << "我是stu的析构函数,我是" << name << endl;
}
shared_ptr <tec> aa;
};
class tec{
public:
string name;
tec(string name) : name(name) {
cout << "我是stu的有参构造函数,我是" << name << endl;
}
~tec() {
cout << "我是stu的析构函数,我是" << name << endl;
}
shared_ptr <stu> aa;
};
int main() {
shared_ptr <stu> student= make_shared <stu> ("Alice");
shared_ptr <tec> teacher = make_shared <tec>("Bob");
teacher->aa = student;
student->aa = teacher;
}
只调用构造不调用析构,很不正常
【引用计数器失灵,双方都在等待对方释放】
使用weak_ptr之后成功解决
weak_ptr 成员函数
没有重载->和*,所以不能直接访问资源
operator=()赋值
已经展示过
exptred()判断是否过期
#include <iostream>
#include <memory>
using namespace std;
class tec;//要先声明tec,不然stu在声明tec时会未定义
class stu {
public:
string name;
stu(string name) : name(name) {
cout << "我是stu的有参构造函数,我是" << name << endl;
}
~stu() {
cout << "我是stu的析构函数,我是" << name << endl;
}
weak_ptr <tec> aa;
};
class tec{
public:
string name;
tec(string name) : name(name) {
cout << "我是stu的有参构造函数,我是" << name << endl;
}
~tec() {
cout << "我是stu的析构函数,我是" << name << endl;
}
weak_ptr <stu> aa;
};
int main() {
shared_ptr <stu> student = make_shared <stu>("Alice");
{
shared_ptr <tec> teacher = make_shared <tec>("Bob");
//Bob仅在代码块内声明,代码块外无Bob
teacher->aa = student;
student->aa = teacher;
//weak_ptr提升为shared_ptr
shared_ptr <tec> flag = student->aa.lock();
if (student->aa.expired())
cout << "语句块内部student->aa过期"<<endl;
else
cout << "语句块内部student->aa.lock()->name="
<< student->aa.lock()->name << endl;
}
shared_ptr <tec> flag = student->aa.lock();
//代码块外部Bob失效
if (student->aa.expired())
cout << "语句块内部student->aa过期" << endl;
else
cout << "语句块内部student->aa.lock()->name="
<< student->aa.lock()->name << endl;
}
lock()返回shared_ptr
lock()函数可以提升为shared_ptr,如果对象活着,返回有效的shared_ptr,如果对象死了,提升失败,返回一个空的shared_ptr
//weak_ptr提升为shared_ptr[线程安全] shared_ptr <tec> flag = student->aa.lock();
reset()置为空|swap()交换|略
总结
- 智能指针不支持指针运算:+,-,++,–
stu类demo
class stu {
public:
stu(string name) : name(name) {
cout << "我是stu的有参构造函数,我是" <<name<< endl;
}
~stu() {
cout << "我是stu的析构函数,我是" << name << endl;
}
string name;
};
weak_ptr线程安全用法
//weak_ptr提升为shared_ptr
shared_ptr <tec> flag = student->aa.lock();
代码块
- 如果在代码块”{}”中定义了变量(定义同一个变量),则该变量的生存周期和作用域将被限制在该代码块内。
如下图
- 若只对变量赋值会改变全局值
下图只有赋值没有重新定义