文章目录
智能指针简介
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer)。在现代 c + + 编程中,标准库包含 智能指针,这些指针用于帮助确保程序不会出现内存和资源泄漏,并具有异常安全。C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件#include<memory>
。
shared_ptr
shared_ptr
使用引用计数,每一个 shared_ptr
的拷贝都指向相同的内存。每引用它一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。
智能指针对比普通指针
#include <iostream>
#include <memory>
using namespace std;
class Data
{
public:
Data() {
cout << "构造" << endl;
}
~Data() {
cout << "析构" << endl;
}
};
int main()
{
cout<<"智能指针:"<<endl;
{
std::shared_ptr<Data> Ptr(new Data); //智能指针出了作用域就会被释放,引用计数减一
}
cout<<"普通指针:"<<endl;
Data* ptr = new Data;
delete ptr;
return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp
ubuntu@VM-16-5-ubuntu:~$ ./test
智能指针
构造
析构
普通指针
构造
析构
基本用法
#include <iostream>
#include <memory>
using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}
int value;
};
int main()
{
std::shared_ptr<Person> p1(new Person(1)); // Person(1)的引用计数为1
std::shared_ptr<Person> p2 = std::make_shared<Person>(2);
p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
// 最后将新对象的指针交给智能指针
std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2
std::cout<<p3.use_count()<<std::endl;
std::cout<<p1.use_count()<<std::endl;
p1.reset();//Person(3)的引用计数为1
//p3.reset();//Person(3)的引用计数为0,析构Person(3)
std::cout<<"over"<<std::endl;
return 0;
}
Cons1
Cons2
Cons3
Des1
2
2
over
Des3
Des2
其他用法
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
int i;
A(int n):i(n) { };
~A() { cout << i << " " << "destructed" << endl; }
};
int main()
{
//错误使用
//A* p = new A(10);
//shared_ptr <A> sp1(p), sp2(p);
shared_ptr<A> sp1(new A(2)); //A(2)由sp1托管,
shared_ptr<A> sp2(sp1); //A(2)同时交由sp2托管
shared_ptr<A> sp3;
sp3 = sp2; //A(2)同时交由sp3托管
cout << sp1->i << "," << sp2->i <<"," << sp3->i << endl;
A * p = sp3.get(); // get返回托管的指针,p 指向 A(2)
cout << p->i << endl; //输出 2
sp1.reset(new A(3)); // reset导致托管新的指针, 此时sp1托管A(3)
sp2.reset(new A(4)); // sp2托管A(4)
cout << sp1->i << endl; //输出 3
sp3.reset(new A(5)); // sp3托管A(5),A(2)无人托管,被delete
cout << "end" << endl;
return 0;
}
2,2,2
2
3
2 destructed
end
5 destructed
4 destructed
3 destructed
reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。
智能指针引用计数为0,释放的对象是
#include <vector>
#include <memory>
#include <iostream>
using namespace std;
class Father
{
public:
Father(std::string name) {}
~Father() {cout<<"父类析构"<<endl;}
std::string m_name;
};
typedef std::shared_ptr< Father > FatherPtr;
class Son:public Father
{
public:
Son(std::string name) :Father(name) { m_name = name; }
~Son() { cout<<"子类析构"<<endl; }
};
typedef std::shared_ptr<Father> FatherPtr;
int main()
{
{
FatherPtr test(new Son("deroy"));
}
cout<<"-------------------------"<<endl;
Father* Ptr = new Son("test");
delete Ptr;
return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp
ubuntu@VM-16-5-ubuntu:~$ ./test
子类析构
父类析构
-------------------------
父类析构
注意事项
- 智能指针管理的是堆上面的指针,(栈上面的地址会造成两次调用析构)
- shared_ptr相当于一个指针,拷贝和赋值会是的引用加一
std::shared_ptr<Person> p1(new Person(1)); // Person(1)的引用计数为1
std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(1),Person(3)的引用计数为2
std::cout<<p3.use_count()<<std::endl; //引用计数为2
std::cout<<p1.use_count()<<std::endl; //引用计数为2
- 只有当引用计数为0时,才会释放内存
/*接上面的代码*/
p1.reset(); //Person(1)的引用计数为1
//p3.reset();//Person(1)的引用计数为0,析构Person(3)
//只重置p1,不重置p3,内存不会释放
- 不要用一个原始指针初始化多个shared_ptr,原因在于,会造成二次销毁,如下所示:
#include <iostream>
#include <memory>
using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}
int value;
};
int main()
{
Person *p5 = new Person(5);
std::shared_ptr<Person> p6(p5);
std::shared_ptr<Person> p7(p5);// logic error
//std::shared_ptr<Person> p7(p6); // 这行是正确的
return 0;
}
Cons5
Des5
Des0
- 避免循环引用
完整代码
#include <iostream>
#include <memory>
using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}
int value;
};
int main()
{
std::shared_ptr<Person> p1(new Person(1)); // Person(1)的引用计数为1
std::shared_ptr<Person> p2 = std::make_shared<Person>(2);
p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
// 最后将新对象的指针交给智能指针
std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2
std::cout<<p3.use_count()<<std::endl;
std::cout<<p1.use_count()<<std::endl;
p1.reset();//Person(3)的引用计数为1
//p3.reset();//Person(3)的引用计数为0,析构Person(3)
std::cout<<"over"<<std::endl;
return 0;
}
Cons1
Cons2
Cons3
Des1
2
2
over
Des3
Des2
智能指针相关的函数总结
成员函数 | 作用 |
---|---|
reset() | 重置智能指针,delete其关联的指针。 |
release() | 不delete关联指针,并返回关联指针。 释放关联指针的所有权,智能指针为空。 |
get() | 仅仅返回关联指针 |
use_count() | 获取引用计数 |
std | 作用 |
---|---|
std::make_unique | 创建 unique_ptr 对象C++14 |
std::make_shared | 创建 make_shared 对象C++14 |
std::move() | 对象转移 |
weak_ptr | 作用 |
---|---|
expired() | 判断智能指针是否失效 |
lock() | 获取其对应的shared_ptr 对象 |
weak_ptr
shared_ptr和
weak_ptr用来解决环形引用的问题
weak_ptr
本身也是一个模板类,但是不能直接用它来定义一个智能指针的对象,只能配合shared_ptr
来使用,可以将shared_ptr
的对象赋值给weak_ptr
,并且这样并不会改变引用计数的值。查看weak_ptr
的代码时发现,它主要有lock
、swap
、reset
、expired
、operator=
、use_count
几个函数,与shared_ptr
相比多了lock
、expired
函数,但是却少了get
函数,甚至连operator*
和 operator->
都没有
其实weak_ptr
本身设计的很简单,就是为了辅助shared_ptr
的,它本身不能直接定义指向原始指针的对象,只能指向shared_ptr
对象,同时也不能将weak_ptr对象直接赋值给shared_ptr
类型的变量,最重要的一点是赋值给它不会增加引用计数:
基本用法
#include <memory>
#include <iostream>
class foo
{
public:
foo()
{
std::cout << "foo construct.." << std::endl;
}
void method()
{
std::cout << "welcome Test foo.." << std::endl;
}
~foo()
{
std::cout << "foo destruct.." << std::endl;
}
};
int main()
{
// weak_ptr
foo* foo2 = new foo();
// share_ptr 管理对象
std::shared_ptr<foo> shptr_foo2(foo2);
// weak_ptr 弱引用
std::weak_ptr<foo> weak_foo2(shptr_foo2);
//由于不知道什么之后weak_ptr所指向的对象就会被析构掉,所以使用之前请先使用expired函数检测一下
if (!weak_foo2.expired())
{
// 如果要获取数据指针,需要通过lock接口获取
weak_foo2.lock()->method();
}
std::shared_ptr<foo> tmp = weak_foo2.lock();
// 这个时候有两个共享指针共享数据,所以use_count数量为2
std::cout<< "share_ptr use_count:"<<shptr_foo2.use_count()<<std::endl;
// 我们这边有尝试多次获取所有权(lock),看一下引用计数个数
// 在获取所有权的时候又构建了一个共享指针,所以此时 use_count 数量为3
std::cout << "shptr_foo2 RefCount: " << weak_foo2.lock().use_count() << std::endl;
return 0;
}
# ./main
foo construct..
welcome Test foo..
share_ptr use_count:2
shptr_foo2 RefCount: 3
foo destruct..
来自:https://www.cnblogs.com/blog-yejy/archive/2018/09/30/9727070.html
常用函数用法
weak_ptr
中只有函数lock
和expired
两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired
函数来检测是否过期,然后使用lock
函数来获取其对应的shared_ptr
对象,然后进行后续操作:
环形引用问题
#include <iostream>
#include <memory>
class Child;
class Parent;
class Parent {
private:
//std::shared_ptr<Child> ChildPtr;
std::weak_ptr<Child> ChildPtr;
public:
void setChild(std::shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
//new shared_ptr
if (this->ChildPtr.lock()) {
}
}
~Parent() {
}
};
class Child {
private:
std::shared_ptr<Parent> ParentPtr;
public:
void setPartent(std::shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {
}
}
~Child() {
}
};
int main() {
std::weak_ptr<Parent> wpp;
std::weak_ptr<Child> wpc;
{
std::shared_ptr<Parent> p(new Parent);
std::shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
std::cout << p.use_count() << std::endl; // 2
std::cout << c.use_count() << std::endl; // 1
std::cout << wpp.use_count() << std::endl; // 2
std::cout << wpc.use_count() << std::endl; // 1
}
std::cout << wpp.use_count() << std::endl; // 0
std::cout << wpc.use_count() << std::endl; // 0
return 0;
}
2
1
2
1
0
0
shared_ptr和weak_ptr详细示例
#include <iostream>
#include <memory>
using namespace std;
class CB;
class CA
{
public:
CA() { cout << "CA() called! " << endl; }
~CA() { cout << "~CA() called! " << endl; }
void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
void show() { cout << "this is class CA!" << endl; }
private:
shared_ptr<CB> m_ptr_b;
};
class CB
{
public:
CB() { cout << "CB() called! " << endl; }
~CB() { cout << "~CB() called! " << endl; }
void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
void show() { cout << "this is class CB!" << endl; }
private:
weak_ptr<CA> m_ptr_a;
};
void test_refer_to_each_other()
{
shared_ptr<CA> ptr_a(new CA());
shared_ptr<CB> ptr_b(new CB());
cout << "a use count : " << ptr_a.use_count() << endl;
cout << "b use count : " << ptr_b.use_count() << endl;
ptr_a->set_ptr(ptr_b);
ptr_b->set_ptr(ptr_a);
cout << "a use count : " << ptr_a.use_count() << endl;
cout << "b use count : " << ptr_b.use_count() << endl;
}
// 测试weak_ptr用法
void test1()
{
// 编译错误 // error C2665: “std::weak_ptr<CA>::weak_ptr”: 3 个重载中没有一个可以转换所有参数类型
// weak_ptr<CA> ptr_1(new CA());
shared_ptr<CA> ptr_1(new CA());
cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 1
shared_ptr<CA> ptr_2 = ptr_1;
cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 2
cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 输出:ptr_1 use count : 2
weak_ptr<CA> wk_ptr = ptr_1;
cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 2
cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 输出:ptr_1 use count : 2
// 编译错误
// error C2440 : “初始化”: 无法从“std::weak_ptr<CA>”转换为“std::shared_ptr<CA>”
// shared_ptr<CA> ptr_3 = wk_ptr;
}
//
// 测试weak_ptr常用函数用法
void test2()
{
shared_ptr<CA> ptr_a(new CA()); // 输出:CA() called!
shared_ptr<CB> ptr_b(new CB()); // 输出:CB() called!
cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 输出:ptr_a use count : 1
cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 输出:ptr_b use count : 1
weak_ptr<CA> wk_ptr_a = ptr_a;
weak_ptr<CB> wk_ptr_b = ptr_b;
if (!wk_ptr_a.expired())
{
wk_ptr_a.lock()->show(); // 输出:this is class CA!
}
if (!wk_ptr_b.expired())
{
wk_ptr_b.lock()->show(); // 输出:this is class CB!
}
// 编译错误
// 编译必须作用于相同的指针类型之间
// wk_ptr_a.swap(wk_ptr_b); // 调用交换函数
wk_ptr_b.reset(); // 将wk_ptr_b的指向清空
if (wk_ptr_b.expired())
{
cout << "wk_ptr_b is invalid" << endl; // 输出:wk_ptr_b is invalid 说明改指针已经无效
}
wk_ptr_b = ptr_b;
if (!wk_ptr_b.expired())
{
wk_ptr_b.lock()->show(); // 输出:this is class CB! 调用赋值操作后,wk_ptr_b恢复有效
}
// 编译错误
// 编译必须作用于相同的指针类型之间
// wk_ptr_b = wk_ptr_a;
// 最后输出的引用计数还是1,说明之前使用weak_ptr类型赋值,不会影响引用计数
cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 输出:ptr_a use count : 1
cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 输出:ptr_b use count : 1
}
int main(int argc, char* argv[])
{
test_refer_to_each_other();
test1();
test2();
getchar();
return 0;
}
a use count : 1
b use count : 1
a use count : 1
b use count : 2
~CA() called!
~CB() called!
CA() called!
ptr_1 use count : 1
ptr_1 use count : 2
ptr_2 use count : 2
ptr_1 use count : 2
ptr_2 use count : 2
~CA() called!
CA() called!
CB() called!
ptr_a use count : 1
ptr_b use count : 1
this is class CA!
this is class CB!
wk_ptr_b is invalid
this is class CB!
ptr_a use count : 1
ptr_b use count : 1
~CB() called!
~CA() called!