内存泄露:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
1.智能指针的使用及原理
1.1 RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if(_ptr)
delete _ptr;
}
private:
T* _ptr;
};
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
throw invalid_argument("除0错误");
return a / b;
}
void Func()
{
ShardPtr<int> sp1(new int);
ShardPtr<int> sp2(new int);
cout << div() << endl;
}
int main()
{
try {
Func();
}
catch(const exception& e)
{
cout<<e.what()<<endl;
}
return 0;
}
1.2 智能指针的原理
上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可
以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其
像指针一样去使用。
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if(_ptr)
delete _ptr;
}
T& operator*() {return *_ptr;}
T* operator->() {return _ptr;}
private:
T* _ptr;
};
struct Date
{
int _year;
int _month;
int _day;
};
int main()
{
SmartPtr<int> sp1(new int);
*sp1 = 10
cout<<*sp1<<endl;
SmartPtr<int> sparray(new Date);
// 需要注意的是这里应该是sparray.operator->()->_year = 2018;
// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
sparray->_year = 2018;
sparray->_month = 1;
sparray->_day = 1;
}
智能指针的原理:
1. RAII特性
2. 重载operator*和opertaor->,具有像指针一样的行为。
1.3C++11和boost中智能指针的关系
-
1.C++ 98 中产生了第一个智能指针auto_ptr.
- 2. C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.
- 3. C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。
- 4. C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。
2.std::auto_ptr管理权转移的思想
学习文档:cplusplus.com/reference/memory/auto_ptr/
3.std::unique_ptr简单粗暴防拷贝
学习文档:cplusplus.com/reference/memory/unique_ptr/
std::unique_ptr
是 C++11 引入的一种智能指针类型,用于确保动态分配的对象在超出作用域时能够自动释放内存,防止内存泄漏。与传统的原始指针不同,std::unique_ptr
拥有对象的唯一所有权,因此不能被复制,只能被移动。
std::unique_ptr
的特点
-
唯一所有权:
std::unique_ptr
拥有所指对象的唯一所有权,即一个对象在任何时刻只能被一个std::unique_ptr
所拥有。复制std::unique_ptr
是不允许的,但可以通过移动将所有权转移到另一个std::unique_ptr
。
-
自动释放内存:
- 当
std::unique_ptr
超出作用域或被显式销毁时,它所拥有的对象会自动被释放,不需要手动调用delete
。
- 当
-
不可复制,可移动:
std::unique_ptr
是不可复制的(即没有复制构造函数或赋值操作符),但可以移动(有移动构造函数和移动赋值操作符)。
#include <iostream>
#include <memory>
class Test {
public:
Test() { std::cout << "Test object created.\n"; }
~Test() { std::cout << "Test object destroyed.\n"; }
};
int main() {
std::unique_ptr<Test> ptr1(new Test()); // 创建一个 unique_ptr
// std::unique_ptr<Test> ptr2 = ptr1; // 错误!unique_ptr 不允许复制
std::unique_ptr<Test> ptr2 = std::move(ptr1); // 将所有权转移给 ptr2
if (!ptr1) {
std::cout << "ptr1 is now null.\n";
}
return 0; // 程序结束时,ptr2 超出作用域,Test 对象会自动销毁
}
/*
输出结果:
Test object created.
ptr1 is now null.
Test object destroyed.
*/
4.std::shared_ptr是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
学习文档:cplusplus.com/reference/memory/shared_ptr/
- shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
- 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
- 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
std::shared_ptr
的特点
-
共享所有权:
- 多个
std::shared_ptr
可以指向同一个对象,它们共享该对象的所有权。每当一个新的std::shared_ptr
被创建时,引用计数会增加;当std::shared_ptr
被销毁时,引用计数会减少。
- 多个
-
引用计数:
std::shared_ptr
通过引用计数来跟踪指向同一个对象的所有指针。当引用计数降为 0 时,对象会自动被销毁。
-
线程安全:
- 增加和减少引用计数的操作是线程安全的,因此
std::shared_ptr
可以安全地在多个线程之间共享。
- 增加和减少引用计数的操作是线程安全的,因此
#include <iostream>
#include <memory>
class Test {
public:
Test() { std::cout << "Test object created.\n"; }
~Test() { std::cout << "Test object destroyed.\n"; }
};
int main() {
std::shared_ptr<Test> ptr1 = std::make_shared<Test>(); // 创建 shared_ptr 并管理 Test 对象
{
std::shared_ptr<Test> ptr2 = ptr1; // ptr2 共享 ptr1 的对象所有权
std::cout << "Reference count: " << ptr1.use_count() << std::endl; // 输出 2
} // ptr2 离开作用域,引用计数减为 1
std::cout << "Reference count: " << ptr1.use_count() << std::endl; // 输出 1
return 0; // ptr1 离开作用域,引用计数减为 0,对象被销毁
}
/*
输出结果:
Test object created.
Reference count: 2
Reference count: 1
Test object destroyed.
*/