前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++】指针与智慧的邂逅:C++内存管理的诗意

【C++】指针与智慧的邂逅:C++内存管理的诗意

作者头像
HZzzzzLu
发布于 2024-12-26 01:05:57
发布于 2024-12-26 01:05:57
14400
代码可运行
举报
文章被收录于专栏:codingcoding
运行总次数:0
代码可运行

RAII

RAII(Resource Acquisition Is Initialization)是一种广泛应用于 C++ 等编程语言中的编程范式,它的核心思想是:资源的获取和释放与对象的生命周期绑定。在 RAII 中,资源(如内存、文件句柄、网络连接等)的获取通常发生在对象的构造函数中,而资源的释放则发生在对象的析构函数中。

这种设计模式确保了资源在不再需要时自动释放,从而避免了手动管理资源的复杂性和潜在的错误(如内存泄漏和资源泄露)。

核心思想

  • 资源获取: 当一个对象被创建时,它会立即获取某个资源。例如,分配内存、打开文件或创建数据库连接等。
  • 资源释放: 当该对象超出作用域或被销毁时,它的析构函数会自动释放相应的资源。这意味着开发者不需要显式地释放资源,降低了出错的概率。

实现方式

  • 构造函数:在对象创建时,负责分配所需的资源。例如,在构造函数中打开一个文件或分配一块内存。
  • 析构函数:在对象销毁时,负责释放该对象占用的资源。当对象的生命周期结束时,析构函数会自动执行,释放资源。

RAII 的优势

  • 自动资源管理: RAII 自动处理资源的释放,不需要显式调用资源释放代码,减少了出错的可能性(如忘记释放资源)。
  • 异常安全: RAII 能够保证即使程序中发生异常,资源也会被正确释放。例如,在 try 块中的对象被销毁时,析构函数会自动释放资源,从而避免资源泄漏。
  • 简洁性和易维护性: 使用 RAII 模式可以使资源管理代码更加简洁和模块化,减少了繁琐的手动管理。
  • 防止内存泄漏: 通过将资源与对象的生命周期绑定,可以有效防止内存泄漏、悬挂指针等问题。

RAII 的缺点

  • 不能自由控制资源释放的时机: 在 RAII 模式中,资源的释放依赖于对象的生命周期,无法显式控制资源的释放时机。如果需要在对象销毁之前释放资源,RAII 可能不适用。
  • 资源生命周期绑定问题: RAII 通过对象生命周期管理资源,这对于某些类型的资源可能不适用。例如,某些外部资源(如数据库连接)可能需要在特定时刻关闭,而不仅仅是在对象销毁时。

RAII 的应用场景

  • 内存管理:例如,unique_ptrshared_ptr 是 C++ 中的智能指针,它们的实现就是基于 RAII 模式,自动管理内存资源。
  • 文件操作:如上文所示,RAII 可以用于文件的打开和关闭,确保即使发生异常,文件资源也会被自动释放。
  • 数据库连接:RAII 可用于数据库连接的管理,确保连接在对象生命周期结束时被自动关闭。
  • 线程锁管理:通过 RAII 模式,锁的获取和释放可以自动管理,避免忘记释放锁导致死锁。

智能指针

智能指针(Smart Pointer 是现代 C++ 中用于自动管理动态内存的一种工具,它通过封装原始指针,提供对内存资源的自动管理,帮助避免常见的内存管理错误,如内存泄漏和悬挂指针。

智能指针实际上是一个类,它重载了指针操作符(如 *->),使得使用智能指针的代码和普通指针一样简便,但它能自动处理资源的释放。

C++标准库中的智能指针都在 <memory> 这个头文件下,智能指针主要有 auto_ptrunique_ptrshared_ptrweak_ptr 等。

auto_ptr

auto_ptr 是C++98标准中引入的一个智能指针类型,通过自动释放资源来避免内存泄漏和悬挂指针的问题。

1. auto_ptr 的缺陷

auto_ptr 的设计存在巨大缺陷,在涉及资源所有权转移时(拷贝或者赋值时)原auto_ptr 不再拥有资源,资源的所有权转移给 目标auto_ptr ,这导致了 原auto_ptr 变成一个悬挂指针(类似于空指针)。

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//模拟一个日期类
struct Date
{
	int _year;
	int _month;
	int _day;

	Date(int year = 2000, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	~Date()
	{
		cout << "~Date" << endl;
	}

};

int main()
{
	auto_ptr<Date> ap1(new Date);
	//拷贝时,Date的管理权限从ap1转移ap2,ap1被置空
	auto_ptr<Date> ap2(ap1);

	//ap1相当于空指针了,再去访问会造成程序崩溃
	//ap1->_day;

	//赋值也是同样的道理,Date的管理权限从ap2转移ap3,ap2被置空
	auto_ptr<Date> ap3;
	ap3 = ap2;
	
	//ap2被置空,访问会造成程序崩溃
	ap2->_day;

	return 0;
}

auto_ptr 的设计存在缺陷,在在涉及资源所有权转移时,其行为会造成意外的错误,auto_ptr在C++11中被废弃,不推荐使用!

2. auto_ptr 的模拟实现 auto_ptr 的模拟实现比较简单,在涉及资源的转移时,将原指针置空即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
class auto_ptr
{
public:
	auto_ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}

	auto_ptr(auto_ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;
	}

	auto_ptr<T>& operator=(auto_ptr<T>& ap)
	{
		if (_ptr != ap._ptr)
		{
			if (_ptr)
				delete _ptr;

			_ptr = ap._ptr;
			ap._ptr = nullptr
		}
		return *this;
	}

	~auto_ptr()
	{
		if (_ptr)
			delete _ptr;
		_ptr = nullptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

unique_ptr

unique_ptr 是独占式的智能指针,表示指向一个动态分配的对象的唯一所有者。该指针不支持拷贝和赋值,但支持移动构造或者赋值。

当一个资源只能有一个拥有者时,使用 unique_ptr 是最合适的选择。

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
unique_ptr<Date> up1(new Date);
//不支持拷贝或者赋值
//unique_ptr<Date> up2(up1);
//unique_ptr<Date> up3; up3 = up1;

//支持移动构造或者赋值,但是ap1置空了,谨慎使用
unique_ptr<Date> up2(move(up1));
unique_ptr<Date> up3;
up3 = move(up2);

1. make_unique make_unique 是 C++11/14 标准库中引入的一个函数模板,用于创建动态分配的对象并返回一个 unique_ptr,从而安全高效地管理对象的生命周期。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);

与直接使用 new 操作符相比的优势

  1. 避免手动调用 newdelete :使用 make_unique 能够简化动态内存分配,避免使用裸指针容易产生的内存泄漏或未定义行为。
  2. 性能优化 :它能够一次性分配对象和控制块所需的内存,减少额外开销。
  3. 强异常安全性:使用 make_unique 时,不会因为对象构造和分配的中间异常而泄漏内存。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//使用 make_unique 创建一个 int 类型的 unique_ptr(推荐)
auto up1 = make_unique<int>(20);

//直接使用 unique_ptr(容易出错),如果构造函数抛异常就会出现内存泄漏
unique_ptr<int> up2(new int(10));

2. 定制删除器

unique_ptr 在释放资源时,默认是 delete _ptr ,如果指向的资源是 new type[num] 而来的,默认释放资源的方式就不适合了,需要 delete[] 的方式是释放资源,这时我们需要定制删除器。

new [] 的方式经常使用,库里已经有了特化版本,而对于定制删除器,仿函数、函数指针、lamba表达式都可作为删除器。

不过要注意的是传定制删除器给 unique_ptr ,是传给模板参数,其构造参数也要传。

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//new []特化
unique_ptr<Date[]> up1(new Date[5]);

// 仿函数作删除器,将其传到模板参数,仿函数构造的对象可以直接调用,不需要传给构造参数
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);

//函数指针作删除器,既要传模板参数也要传构造参数
unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);

// lambda表达式作删除器,decltype获取delArr的类型
auto delArr = [](Date* ptr) {delete[] ptr; };
unique_ptr<Date, decltype(delArr)> up4(new Date[5], delArr);

简单说一下定制删除器的底层,将定制删除器的类型传过去利用其类型创建删除器对象并用传给构造参数的具体定制删除器对象来初始化,这样底层就有了外层传进来的定制删除器,然后利用删除器释放资源

3. unique_ptr 的模拟实现

unique_ptr 的模拟实现也比较简单,将其构造函数赋值重载函数 delete 即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
class unique_ptr
{
public:
	//防止隐式类型转换
	explicit unique_ptr(T* ptr)
		:_ptr(ptr)
	{}

	~unique_ptr()
	{
		if (_ptr)
			delete _ptr;
		_ptr = nullptr;
	}

	unique_ptr(const unique_ptr<T>& up) = delete;
	unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;

	unique_ptr(unique_ptr<T>&& up)
		:_ptr(up._ptr)
	{
		up._ptr = nullptr;
	}

	unique_ptr<T>& operator=(unique_ptr<T>&& up)
	{
		delete _ptr;
		_ptr = up._ptr;
		up._ptr = nullptr;
	}

private:
	T* _ptr;
};

有关 explicit

关键字 explicit 的作用是修饰构造函数,防止隐式类型转换

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Date* ptr = new Date;
//hz::unique_ptr<Date> up1 = ptr; 这种写法编译会出错,explicit不允许隐式类型转换,本质是构造+拷贝
unique_ptr<Date> up1(ptr);

使用 explicit 修饰单参数构造函数可以提高代码的可读性,减少维护负担。

shared_ptr

shared_ptr 是 C++11 标准引入的一种智能指针,用于管理动态分配的对象,并允许多个 shared_ptr 实例共享同一对象的所有权。shared_ptr 使用 引用计数 来追踪有多少个 shared_ptr 对象共享资源,并在最后一个 shared_ptr 被销毁时自动释放资源。这种机制确保了内存管理的安全性,避免了内存泄漏,同时允许多个对象共享相同的资源。

shared_ptr 是一种 共享所有权 的智能指针,而非独占所有权(像 unique_ptr )。多个 shared_ptr 对象可以共同管理一个动态分配的对象,而不必担心资源的重复释放或遗漏释放。

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//创建一个 shared_ptr 管理 Date 对象
shared_ptr<Date> sp1(new Date);

//复制sp1,增加引用计数
shared_ptr<Date> sp2(sp1);
cout << "Reference count: " << sp1.use_count() << endl;

//当 sp1 和 sp2 超出作用域时,Date 对象会被自动销毁

代码解析: 1.shared_ptr<Date> sp1(new Date);

  • sp1 是一个 shared_ptr,它管理一个动态分配的 Date 对象。此时引用计数为 1。

2.shared_ptr<Date> sp2(sp1);

  • sp2sp1 的副本,意味着它也指向同一个 Date 对象,引用计数增加到 。

3.sp1.use_count() 返回当前有多少个 shared_ptr 管理相同的对象。此时返回 2。

4.当 sp1sp2 超出作用域时,它们的引用计数都会减少。当引用计数降到 0 时,Date 对象会自动销毁。

有关 make_shared

make_shared 也是一个函数模板,用于创建共享指针,可以接受任何类型的参数,并返回一个指向该类型对象的共享指针。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <class T, class... Args>
  shared_ptr<T> make_shared (Args&&... args);
  • make_shared 与直接使用 shared_ptr 的对比

特性

make_shared

直接用shared_ptr

语法简洁性

更简洁

需要手动调用 new

内存分配次数

1 次

2 次(对象和引用计数分别分配)

异常安全性

更安全

容易出现内存泄漏

模拟实现

对于 shared_ptr 的模拟实现,我们首先要考虑的就是引用计数的设计。

引用计数用静态成员变量是无法实现的

因为静态成员变量是整个类共有的,每当指向一个资源,无论是不同的资源还是相同的资源,静态成员变量都会增加,不能做到对于不同的资源都有独立的一份引用计数。 比如 sp1 和 sp2 指向着资源1,引用计数是2,在创建一个 sp3 指向资源2,由于引用计数是静态成员变量,引用计数就变成3了,这显然是错误的,sp3 的引用计数应该是1.

  • 引用计数的设计应该采用动态开辟的方式,做到每一个不同的资源都有一份独立的引用计数。

以下为 shared_ptr 的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
class shared_ptr
{
public:
	//explicit防止隐式类型转换
	explicit shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{}

	~shared_ptr()
	{
		if (--(*_pcount) == 0)
		{
			delete _ptr;
			delete _pcount;
		}
	}

	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcount(sp._pcount)
	{
		++(*_pcount);
	}

	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			if (--(*_pcount) == 0)
			{
				delete _ptr;
				delete _pcount;
			}

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			++(*_pcount);
		}
		return *this;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	 T* get() const
	 {
	 	 return _ptr;
	 }
	 
 	int use_count() const
     {
         return *_pcount;
     }
	
private:
	T* _ptr;
	int* _pcount;
};

shared_ptr 的成员函数的实现都比较简单,但是赋值重载函数有比较多细节要注意:

  1. 赋值操作要保证不是一个指针自己给自己赋值this != &sp 不能完全处理所有情况,因为不同的 shared_ptr 对象的 _ptr 可能是一样的,得用 _ptr != sp._ptr 才可以完全覆盖所有情况。
  2. 被赋值的指针的引用计数要先要减1判断该指针是否是最后一个指向对应资源的指针若是则要释放原来的资源
  3. 进行赋值操作,完成后引用计数要+1,最后返回 *this
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
	if (_ptr != sp._ptr)
	{
		if (--(_pcount) == 0)
		{
			delete _ptr;
			delete _pcount;
		}

		_ptr = sp._ptr;
		_pcount = sp._pcount;
		++(*_pcount);
	}
	return *this;
}

定制删除器

shared_ptr 也可以传定制删除器,不过相比 unique_ptr 的方式,shared_ptr 传递删除器的方式只需传到构造函数的参数即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//其构造函数的声明
template <class U, class D> shared_ptr (U* p, D del);
template <class D> shared_ptr (nullptr_t p, D del);

使用示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
struct DeleteArray
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};

template<class T>
void DeleteArrayFunc(T* ptr)
{
	delete[] ptr;
}

//[]特化版本
shared_ptr<int[]> sp1(new int[10]);

//仿函数作删除器
shared_ptr<Date> sp2(new Date[10], DeleteArray<Date>());

//函数指针作删除器
shared_ptr<Date> sp3(new Date[10], DeleteArrayFunc<Date>);

//lambda作删除器
auto  delArr = [](Date* ptr) {delete[] ptr; };
shared_ptr<Date> sp4(new Date[10], delArr);

增加定制删除器的模拟实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
class shared_ptr
{
public:
	//explicit防止隐式类型转换
	explicit shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{
	}

	template<class D>
	shared_ptr(T* ptr, D del)
		:_ptr(ptr)
		, _pcount(new int(1))
		,_del(del)
	{}

	~shared_ptr()
	{
		if (--(*_pcount) == 0)
		{
			_del(_ptr);
			delete _pcount;
		}
	}

	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcount(sp._pcount)
		, _del(sp._del)
	{
		++(*_pcount);
	}

	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			if (--(*_pcount) == 0)
			{
				_del(_ptr);
				delete _pcount;
			}

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			_del = sp._del;
			++(*_pcount);
		}
		return *this;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
	int* _pcount;
	//利用function来包装 _del,默认是不带[] 的delete
	function<void(T*)> _del = [](T* ptr) {delete ptr; };
};

代码细节:

  • 写多一个构造函数并套一层模板,当传递删除器的时候,调用此函数。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class D>
	shared_ptr(T* ptr, D del)
		:_ptr(ptr)
		, _pcount(new int(1))
		,_del(del)
	{}
  • 删除器可以是仿函数、函数指针和 lambda 表达式等,我们是没有办法用具体的某个类型去创建 _del 变量,但是C++11中有一个类模板 function ,它是通用的函数包装器,可以包装仿函数、函数指针和 lambda 表达式,而删除器的函数签名都是 void(T* ptr)(返回类型和参数类型)。我们就可以用 function 来创建 _del 变量,并给上 lambda 缺省值 [](T* ptr) {delete ptr; }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//利用function来包装 _del,默认是不带[] 的delete
	function<void(T*)> _del = [](T* ptr) {delete ptr; };

循环引用 和 weak_ptr

智能指针是用来管理动态分配的内存,以避免内存泄漏的问题。然而,如果使用不当,智能指针也会引入一些新的问题,例如循环引用

循环引用(Cyclic Reference)是指两个或多个对象互相持有对方的引用形成一个环导致它们无法被释放,即使它们已经不再被其他部分使用。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Node {
public:
	//指向下一个节点的智能指针
	shared_ptr<Node> next;
	~Node() { cout << "Node destroyed" << endl; }
};

int main() {
	auto node1 = make_shared<Node>();
	auto node2 = make_shared<Node>();

	// 相互引用
	node1->next = node2;
	node2->next = node1;

	// 程序结束时,node1和node2不会被释放
	return 0;
}

为什么会出现循环引用?

  1. shared_ptr 的原理:
    • shared_ptr 通过引用计数来管理对象的生命周期。
    • 当引用计数变为0时,shared_ptr 会自动释放内存。
  2. 循环引用的本质:
    • 在上述例子中,node1node2 互相持有对方的 shared_ptrnode1 需要 node2的shared_ptr 析构时释放,而 node2的shared_ptrnode2 的成员变量,需要让 node2 释放才会析构,node2 需要 node1的shared_ptr 析构时释放, node1的shared_ptr 需要让 node1 释放才会析构。这样就形成一个环了,两个节点的 shared_ptr 的引用计数始终不为0;
    • 即使它们超出了作用域,也不会被销毁,从而引发内存泄漏

如何解决循环引用问题?

可以通过将其中一个 shared_ptr 替换为 weak_ptr来打破循环引用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Node {
public:
	// 用weak_ptr打破循环引用
	 weak_ptr<Node> next;
	~Node() { cout << "Node destroyed" << endl; }
};

int main() {
	auto node1 = make_shared<Node>();
	auto node2 = make_shared<Node>();

	// weak_ptr不会增加引用计数
	node1->next = node2;
	node2->next = node1;

	// 程序结束时,node1和node2会被正确释放
	return 0;
}

程序结束时,先析构 node2 ,引用计数减到0,释放 node2 ,而不会像循环引用那般由于 node2 和 node1->next 指向同一个对象,析构 node2 时其引用计数从2减到1,导致引用计数永远不为0导致 node2 无法释放。node2 释放后 node1 也能正常释放了

weak_ptr 是一种辅助智能指针,它与 shared_ptr 配合使用,用于解决循环引用问题实现对象的非强拥有关系

  • weak_ptr 是一种不参与引用计数的智能指针。
  • 它不会改变所指向对象的生命周期,仅仅是一个“弱引用”。
  • 常用于观察由 shared_ptr 管理的对象,而不会影响其销毁时机。

基本用法:

  • weak_ptr 必须从 shared_ptr 初始化,不能直接管理动态分配的内存。
  • 通过 weak_ptr 无法直接访问对象,需要调用 lock() 方法将其转换为 shared_ptrlock() 方法返回一个指向相同对象的 shared_ptr,如果对象已被释放,则返回一个空指针。

shared_ptr 的区别

特性

shared_ptr

weak_ptr

是否参与引用计数

是否影响生命周期

访问对象方式

直接使用 * 或 ->

需调用 lock() 转换为 shared_ptr

应用场景

强拥有关系,负责对象生命周期

弱引用,避免循环引用或临时访问

总结

  • weak_ptr 是一种轻量级的智能指针,用于观察对象,不参与对象生命周期管理。
  • 在设计需要临时引用或防止循环引用的场景中,weak_ptr 是一个非常重要的工具。
  • 配合 shared_ptr 使用,能够更好地管理复杂对象间的依赖关系。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++智能指针
C++智能指针 零、前言 一、为什么需要智能指针 二、内存泄漏 三、智能指针 1、RAII 2、智能指针的原理 3、std::auto_ptr 4、std::unique_ptr 5、std::shared_ptr 6、std::weak_ptr 7、删除器 8、C++11和boost中智能指针的关系 零、前言 本章主要讲解学习C++中智能指针的概念及使用 一、为什么需要智能指针 示例: double Division(int a, int b) { // 当b == 0时抛出异常 if (b =
用户9645905
2022/11/15
6650
C++智能指针
【C++从小白到大牛】C++智能指针的使用、原理和分类
这里如果是p1的new抛异常了 那么首先p1是申请空间失败的 然后程序跳转到异常处理机制,如果main函数中没有对异常捕捉,那么程序就终止了。
用户11316056
2024/10/16
1360
【C++从小白到大牛】C++智能指针的使用、原理和分类
C++智能指针详解
智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏和空悬指针等等问题。
小灵蛇
2024/06/06
1770
C++智能指针详解
C++智能指针
什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
二肥是只大懒蓝猫
2023/03/30
7750
C++智能指针
【C++】智能指针
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
秦jh
2024/12/31
1290
【C++】智能指针
[C++] 智能指针
在现代C++开发中,资源管理(包括内存、文件句柄、锁等)是一个至关重要的问题。特别是在异常安全性设计中,如何避免资源泄漏是开发者必须面对的挑战。
DevKevin
2024/11/17
3880
[C++] 智能指针
C++:智能指针
在学习异常的时候,我们知道了由于异常的反复横跳可能会导致内存泄露的问题,但是对于一些自定类类型来说他在栈帧销毁的时候会去调用对应的析构函数,但是以下这种必须手动释放的场景,一旦抛出异常就会造成内存泄露的结果。
小陈在拼命
2024/05/27
1460
C++:智能指针
【C++】智能指针
在C++异常学习的部分,我们也发现异常也有很多问题,例如我们先分析一下下面这段程序的问题:
YoungMLet
2024/03/01
1900
【C++】智能指针
【C++11】智能指针
new空间也有可能会抛出异常,对于p1如果抛出异常:没有问题,可以不管,直接到最外面去了。
平凡的人1
2023/10/15
2550
【C++11】智能指针
【C++进阶学习】第十三弹——C++智能指针的深入解析
在C++中,指针是用于访问内存地址的一种特殊变量。传统的指针管理需要程序员手动分配和释放内存,这容易导致以下问题:
GG Bond1
2024/08/14
1430
【C++进阶学习】第十三弹——C++智能指针的深入解析
【C++】一文深入浅出带你参透库中的几种 [ 智能指针 ]及其背后实现原理(代码&图示)
YY的秘密代码小屋
2024/02/03
5230
【C++】一文深入浅出带你参透库中的几种 [ 智能指针 ]及其背后实现原理(代码&图示)
【C++高阶】:智能指针的全面解析
📒除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称作自由空间(free store)或堆(heap)。程序用堆来存储动态分配(dynamically allocate)的对象。动态对象的生存期由程序来控制,也就是说,当动态对象不再使用时,我们的代码必须显式地销毁它们。
IsLand1314
2024/10/15
4140
【C++高阶】:智能指针的全面解析
【C++】C++的内存处理 --- 智能指针
我们来回顾一下在学习异常机制中遇到的一种问题:在try catch语句中,如果我们开辟了一段空间,但是发生了异常,会直接终止掉函数栈桢,导致内存泄漏问题。所以此时就要在catch语句中进行一个特殊处理。如果我们开辟了多段空间,那么这个操作就会变得更加复杂:假如new失败了,就会直接返回到上层的catch语句,也导致了内存泄漏问题!使用传统是异常机制来解决问题会产生大量冗余的语句 — 大量的try catch嵌套!
叫我龙翔
2024/08/13
1840
【C++】C++的内存处理 --- 智能指针
【C++】智能指针
下⾯程序中我们可以看到,new了以后,我们也delete了,但是因为抛异常导,后⾯的delete没有得到执⾏,所以就内存泄漏了,所以我们需要new以后捕获异常,捕获到异常后delete内存,再把异常抛出,但是因为new本⾝也可能抛异常,连续的两个new和下⾯的Divide都可能会抛异常,让我们处理起来很⿇烦。智能指针放到这样的场景⾥⾯就让问题简单多了。
用户11290673
2025/02/14
710
【C++】智能指针
【c++11】智能指针 -- 摆脱内存困扰,现代编程的智能选择
在软件开发的世界里,资源的有效管理至关重要,尤其是在处理动态分配的内存时。稍不留神,内存泄漏就会像潜伏的幽灵,悄无声息地消耗系统资源,最终导致程序崩溃或性能下降。
ephemerals__
2025/05/27
1140
【c++11】智能指针 -- 摆脱内存困扰,现代编程的智能选择
智能指针:作用 | 使用 | 原理 | 内存泄漏
由于异常的存在,反复横跳可能会导致内存泄漏,不同的异常处理逻辑没有妥善管理内存分配和释放,可能会在某些路径中遗漏delete 操作,从而导致内存泄漏。
南桥
2024/09/25
1820
智能指针:作用 | 使用 | 原理 | 内存泄漏
智能指针的讲解
首先我们分析一段代码: 1、如果p1这里new 抛异常会如何? 2、如果p2这里new 抛异常会如何? 3、如果div调用这里又会抛异常会如何?
ahao
2024/03/19
1410
智能指针的讲解
【C++】智能指针
若p2处new抛异常,则相当于p2的new没有成功,而p1的new成功了,所以需要释放p1,然后再重新抛出
lovevivi
2023/10/17
1950
【C++】智能指针
【C++】智能指针 && 守卫锁
​ 上述代码中,main 函数中捕获 Func 的异常,而非常关键,因为可能会导致一些内存泄漏的问题,new 会去调用 operator[],而这是有可能会申请失败的,一旦失败就会抛异常被 main 函数捕捉到,那么 Func 下面的 delete 就没有被执行到,这不是妥妥的内存泄漏吗❓❓❓
利刃大大
2025/03/03
970
【C++】智能指针 && 守卫锁
【c++】智能指针详解&&c++特殊类设计&&c++的类型转换
下面我们先分析一下下面这段程序有没有什么内存方面的问题?提示一下:注意分析MergeSort函数中的问题
用户10925563
2024/08/06
2550
【c++】智能指针详解&&c++特殊类设计&&c++的类型转换
相关推荐
C++智能指针
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验