目录
- 一、内存泄漏
- 二、智能指针概述
- 三、auto_ptr
- 四、unique_ptr
- 五、shared_ptr
- 六、weak_ptr
- 七、引用
-
一、内存泄漏
- 什么是内存泄漏:指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
- 内存泄漏发生原因:内存泄漏主要发生在堆内存分配方式中,即“配置了内存后,所有指向该内存的指针都遗失了”;若缺乏语言这样的垃圾回收机制,这样的内存片就无法归还系统
- 内存泄漏排查方式:因为内存泄漏属于程序运行中的问题,无法通过编译识别,所以只能在程序运行过程中来判别和诊断
- 运行如下代码:在任务管理器查看如下代码的程序,可以发现,内存一直在增加不会减少
#include
int main()
{
while (true)
{
int* wp = new int(10);
}
return 0;
}
二、智能指针概述
- 使用指针存在的问题:使用指针是非常危险的行为,可能存在空指针、野指针问题,并可能造成内存泄漏问题;可指针又非常的高效,所以我们希望以更安全的方式来使用指针
- 安全使用指针的解决方案
- 使用更安全的指针 —— 智能指针
- 不使用指针,使用更安全的方式 —— 引用
- C++中四种常用的智能指针:unique_ptr、shared_ptr、weak_ptr和auto_ptr(C++11中已经废弃的deprecated,在C++17中被正式删除)
三、auto_ptr
- auto_ptr使用:由new expression或得对象,在auto_ptr对象销毁时,他所管理的对象也会自动被delete掉
- 所有权转移:
- 不小心把它传递给另外的智能指针,原来的指针就不再拥有这个对象了
- 在拷贝/赋值过程中,会直接剥夺指针对原对象对内存的控制权,转交给新对象,然后再将原对象指针置为nullptr
#include
using namespace std;
int main()
{
{
auto_ptr<int> pI(new int(10));
cout << *pI << endl;
auto_ptr<string> languages[5] = {
auto_ptr<string>(new string("C")),
auto_ptr<string>(new string("Java")),
auto_ptr<string>(new string("C++")),
auto_ptr<string>(new string("Python")),
auto_ptr<string>(new string("Rust"))
};
cout << "There are some computer languages here first time: \n";
for (int i = 0; i < 5; ++i)
{
cout << *languages[i] << endl;
}
auto_ptr<string> pC;
pC = languages[2];
cout << "There are some computer languages here second time: \n";
for (int i = 0; i < 5; ++i)
{
cout << *languages[i] << endl;
}
cout << "The winner is " << *pC << endl;
}
return 0;
}
![6、C++指针(三):智能指针与内存泄漏_第1张图片](http://img.e-com-net.com/image/info8/d36c8373245d44c594a8ff849fbc9c24.jpg)
四、unique_ptr
- unique_ptr概念:unique_ptr是专属所有权,所以unique_ptr管理的内存,只能被一个对象持有,不支持复制和赋值
- 移动语义:unique_ptr禁止了拷贝语义,但有时我们也需要能够转移所有权,于是提供了移动语义,即可以使用std::move()进行控制所有权的转移
#include
using namespace std;
int main()
{
{
auto i = unique_ptr<int>(new int(10));
cout << *i << endl;
}
auto w = std::make_unique<int>(10);
cout << *(w.get()) << endl;
auto w2 = std::move(w);
cout << ((w.get() != nullptr) ? (*w.get()) : -1) << endl;
cout << ((w2.get() != nullptr) ? (*w2.get()) : -1) << endl;
return 0;
}
五、shared_ptr
- shared_ptr简介:shared_ptr是为了解决auto_ptr在对象所有权上的局限性,在使用引用计数的机制上提供了可以共享所有权的智能指针,当然这需要额外的开销
- shared_ptr原理
- shared_ptr通过一个引用计数共享一个对象
- 当引用计数为0时,该对象没有被使用,可以进行析构
#include
using namespace std;
int main()
{
{
auto wA = shared_ptr<int>(new int(20));
{
auto wA2 = wA;
cout << ((wA2.get() != nullptr) ? (*wA2.get()) : -1) << endl;
cout << ((wA.get() != nullptr) ? (*wA.get()) : -1) << endl;
cout << wA2.use_count() << endl;
cout << wA.use_count() << endl;
}
cout << wA.use_count() << endl;
cout << ((wA.get() != nullptr) ? (*wA.get()) : -1) << endl;
}
auto wAA = std::make_shared<int>(30);
auto wAA2 = std::move(wAA);
cout << ((wAA.get() != nullptr) ? (*wAA.get()) : -1) << endl;
cout << ((wAA2.get() != nullptr) ? (*wAA2.get()) : -1) << endl;
cout << wAA.use_count() << endl;
cout << wAA2.use_count() << endl;
return 0;
}
- shared_ptr的循环引用问题:引用计数会带来循环引用的问题,循环引用会导致堆里的内存无法正常回收,造成内存泄漏
![6、C++指针(三):智能指针与内存泄漏_第2张图片](http://img.e-com-net.com/image/info8/c3d7ea74304d4fe4b10b1f867d542ca7.jpg)
六、weak_ptr
- weak_ptr简介:weak_ptr被设计为与shared_ptr共同工作,用一种观察者模式工作
- weak_ptr作用:协助shared_ptr工作,可获得资源的观测权,像旁观者那样观测资源的使用情况;观察者意味着weak_ptr只对shared_ptr进行引用,而不改变其引用计数;当被观察的shared_ptr失效后,相应的weak_ptr也相应失效
- 实例说明
- BW使用的是weak_ptr,所以test2的tA.use_count()=1
- 并且查看最后的输出可以发现
~A()
和~B()
都没有执行,~AW()
和~BW()
都执行了析构
#include
using namespace std;
struct B;
struct A {
shared_ptr<B> pb;
~A()
{
cout << "~A()" << endl;
}
};
struct B {
shared_ptr<A> pa;
~B()
{
cout << "~B()" << endl;
}
};
struct BW;
struct AW
{
shared_ptr<BW> pb;
~AW()
{
cout << "~AW()" << endl;
}
};
struct BW
{
weak_ptr<AW> pa;
~BW()
{
cout << "~BW()" << endl;
}
};
void Test()
{
cout << "Test shared_ptr and shared_ptr: " << endl;
shared_ptr<A> tA(new A());
shared_ptr<B> tB(new B());
cout << tA.use_count() << endl;
cout << tB.use_count() << endl;
tA->pb = tB;
tB->pa = tA;
cout << tA.use_count() << endl;
cout << tB.use_count() << endl;
}
void Test2()
{
cout << "Test weak_ptr and shared_ptr: " << endl;
shared_ptr<AW> tA(new AW());
shared_ptr<BW> tB(new BW());
cout << tA.use_count() << endl;
cout << tB.use_count() << endl;
tA->pb = tB;
tB->pa = tA;
cout << tA.use_count() << endl;
cout << tB.use_count() << endl;
}
int main()
{
Test();
Test2();
return 0;
}
![6、C++指针(三):智能指针与内存泄漏_第3张图片](http://img.e-com-net.com/image/info8/2ad720a62a3b45cd936d734b5c008d53.png)
七、引用
- 引用是什么:是一种特殊的指针,不允许修改的指针
- 使用指针的坑
- ①.空指针
- ②.野指针
- ③.不知不觉改变了指针的值,却继续使用
- 使用引用,则可以
- ①.不存在空引用
- ②.必须初始化
- ③.一个引用永远指向它初始化的那个对象
- 引用的基本使用:可以认为是指定变量的别名,使用时可以认为是变量本身
#include
using namespace std;
int main()
{
int x = 1, x2 = 3;
int& rx = x;
rx = 2;
cout << x << endl;
cout << rx << endl;
rx = x2;
cout << x << endl;
cout << rx << endl;
return 0;
}
#include
#include
using namespace std;
void swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
void swap2(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 3, b = 4;
swap(a, b);
assert(a == 4 && b == 3);
a = 3, b = 4;
swap2(&a, &b);
assert(a == 4 && b == 3);
return 0;
}
- 为什么有了指针还需要引用?:Bjarne Stroustrup解释 —— 为了支持函数运算符重载
- 为什么有了引用还需要指针?:Bjarne Stroustrup解释 —— 为了兼容C语言
关于函数传递参数类型
- 对内置基础类型(如int,double等)而言:在函数中传递pass by value(传值)更高效
- 对OO面向对象中自定义类型而言:在函数中传递pass by reference to const(const的引用)更高效