C/C++相关概念和易错知识点(55)(异常、智能指针)

《C/C++实战专栏》介绍 本文详细介绍《C/C++实战专栏》的主体内容构成。 阅读详情

1.异常

在C语言这样的面向过程编程的语言来说,处理错误一般有两种方式:终止程序,如assert;返回错误码,比如打开文件失败,errno就会改变,perror就会打印错误码对应的错误信息

在面向对象的语言中,异常是更常见的处理错误的方式,如连接服务器我们不会一次就成功,需要多尝试几次,不能一失败就终止程序。errno也过于局限,错误码的分配有限,不能自定义,同时错误码还必须及时手动处理,否则会被覆盖

(1)处理方式

先执行try内部语句,内部可能有throw抛异常,catch捕获异常并处理

我们可以看到,先执行try里面的语句,在fun()里遇到了throw语句抛出异常,抛出异常后终止后续所有代码的执行,之后catch捕获后进行处理。只有抛了异常才会走catch,如果不抛异常就不会走catch。抛异常是传值返回,返回的是拷贝的对象,返回右值会调用移动构造简化。

下面是一个更直观的例子,可以更好理解使用规则

(2)详细规则

在try语句内部(try中调用函数也算作在try代码块中,如上面的例子)抛出异常后,会直接跳转到catch语句,跳转规则是会沿着函数栈帧(调用函数的顺序)层层往回找,先看抛异常的语句在不在try代码块中,再看抛出的异常的类型有没有匹配当前catch,如果匹配了就会执行当前catch语句以及后面的语句,返回上层栈帧后如果本身还在try内部,那么不会进入任何catch语句,就算有匹配的,可以理解为抛出的异常用一次就销毁了。

#include <iostream>
using namespace std;

void fun2()
{
	throw "fun2()";
}

void fun1()
{
	try
	{
		fun2();
	}
	catch(const char* msg)
	{
		cout << "fun1()" << endl;
	}

	cout << "fun1()" << endl;
}

int main()
{
	try
	{
		fun1();
	}
	catch (const char* msg)
	{
		cout << "main()" << endl;
	}

	return 0;
}

结果是

我们可以看到fun2抛出异常后,会层层往上找,fun2是在fun1的try语句内,所以会被fun1的catch捕获,catch后的语句正常执行,再往上走发现fun1和fun2其实都是在main函数的try语句内,但是总共就抛出一次异常并被解决了,所以后面就不会执行catch语句

注意层层向上匹配时需要严格匹配,抛出的常量字符串不会被char*捕获,也不能用string捕获,如果抛出int异常自然也不会转为size_t。所以fun1中的catch语句及之后的代码不会执行,而会匹配main函数中的catch语句。抛异常后的下一句执行代码一定是向上找第一次完美匹配的catch语句的第一行代码

如果到了main函数还是找不到对应的catch,即抛出的异常没有被处理,就会直接终止程序,编译器认为异常没有被处理一定是存在问题的。

所以我们要保证所有的异常能得到处理,我们可以使用catch(...)兜底,catch(...)能在其它catch语句匹配不上时派上用场,至少不会让程序直接终止

有个小细节,即catch(...)只能放在最后作为兜底,不过也几乎没人这么做,这里提一下

(3)子类异常用父类捕获

先看看下面的代码,顺便复习复习继承和多态


#include <iostream>
using namespace std;

class B;

class A
{
public:
	virtual B& CreateMessage(int id = 1, const string& errmsg = "error") = 0
	{}

	int _id;
	string _errmsg;
};

class B : public A 
{
public:
	B& CreateMessage(int id, const string& errmsg)
	{
		_id = id;
		_errmsg = errmsg;
		return *this;
	}
};

int main()
{
	try
	{
		A* throwmsg = new B;

		throw throwmsg->CreateMessage();
	}
	catch (A& msg)
	{
		cout << msg._id << ":" << msg._errmsg << endl;
	}

	return 0;
}

结果是

首先使用父类指针指向子类空间构成多态,虚函数表存的CreateMessage()是B中的,当以A指针调用函数时匹配的是B的内容,但是多态中,都是以父类声明+子类定义调用函数,所以不需要传参,用纯虚函数的缺省值就可以了,实际走的代码还是B中的。

抛出的B可以被A&捕获,这其实也和赋值兼容转换结合起来了,同时也再次强调赋值兼容转换不是类型转换,因为catch是严格匹配的,不允许赋值兼容转换,因此这在逻辑上是合理的。

(4)异常规范

确定不会抛异常的在函数后面加noexcept,这样能很好规范异常的使用

写了noexcept后就算再抛异常也不会编译报错,但是noexcept会影响编译器逻辑,运行时不会捕获抛出的异常,就算看上去能匹配catch也会报错,这也相当于另一种规范

(5)标准库异常体系

当我们调用库中的函数出了问题时,就会抛出异常,我们可以用const exception& e接收异常,exception是一个类,我们可以用成员函数e.what()得到具体错误信息

下面是常见的用法

(6)C++异常缺点

异常使用频繁会导致代码执行位置乱跳,标准库的异常体系并不是太好用,一般来说都是自定义异常体系,同时noexcept也不是硬性规定。

但是最大的问题还是安全问题,即抛出异常后后续代码都会终止,这可能会导致已开辟的空间没有办法delete,出现内存泄漏,这极难控制,需要引入更复杂的解决办法,后续会讲到。

我为这个问题举个例子

如果arr1开辟失败要抛异常,如果arr2开辟失败也要开辟异常,arr3同理,而且还可能出现arr1和arr2都开辟好了,但arr3开辟失败,这个时候还要处理arr1和arr2的释放。我们发现要处理的异常极多,根本没有办法涵盖所有情况,所以我们用常规思维解决不了内存泄漏的问题。

2.智能指针(RAII思想)

RAII思想是实现智能指针的核心,它利用了C++局部对象自动销毁的特性(类的对象自动调用析构函数)来控制资源的生命周期,就能很好地防止内存泄漏,下面举个简单的例子

当调用函数时,创建了Ptr类的对象,资源获得立即初始化,这个对象掌管着堆区开辟的数组,当返回函数栈帧时,就会自动调用析构函数,把资源释放掉,因此我们堆区开辟的空间就不会泄露了

这本质上是借助对象的生命周期来控制程序资源,使用模板就可以管理任意类型的资源,智能指针就是按照这个思路来实现的,相当于在原本管理数据的int*外再包一层,这样就能解决异常乱跳导致的问题。

(1)unique_ptr

用法:unique_ptr<int> up(new int)

就和我们前面的使用一样,unique_ptr本质也是利用RAII思想实现的类,靠着类的生命周期来防止开辟的空间无法被delete,就算我们不知道unique_ptr具体实现,但是也能很快弄清具体的功能,直接用unique_ptr开辟空间也要安全得多。

智能指针还能模仿原生指针的相关操作,如operator*和operator->(和迭代器的实现一样)

因此,堆区动态开辟空间应尽量交给智能指针管理。

智能指针难度在于拷贝,我们所需的拷贝是浅拷贝,而不是深拷贝,因为它是要模仿指针的拷贝,不同的智能指针可以指向同一块空间,但问题在于析构多次会导致越界访问。unique_ptr的特点就是禁止任何拷贝和赋值,一片空间只能交给一个unique_ptr管理。

拷贝构造和赋值重载都被delete掉,或是使用了private修饰,不能显式调用,也就实现了禁止拷贝赋值的操作,进而也就不会出现同一块空间用多个unique_ptr管理的情况。

在不需要同一块空间用多个unique_ptr管理时,可以多使用unique_ptr。

unique_ptr还需要处理自定义类型,如数组的开辟

第一种解决办法就是在模板参数类型后面加[ ]

第二种办法就是手动实现仿函数(删除器)

先看一下下面的代码,想一想是怎样使用的。


#include <iostream>
#include <vector> 
using namespace std;

template<class T>
class DeleteArray
{
public:
	void operator()(T* t)
	{
		cout << "DeleteArray" << endl;
		delete[] t;
	}
};

class DeleteFile
{
public:
	void operator()(FILE* f)
	{
		cout << "DeleteFile" << endl;
		if (f)
			fclose(f);
	}
};

class A
{
public:
	~A()
	{
		cout << "~A()" << endl;
	}
	int _a = 10;
};

int main()
{

	unique_ptr<FILE, DeleteFile> up1(fopen("test.txt", "r"));
	unique_ptr<A, DeleteArray<A>> up2(new A[10]);

	return 0;
}

结果是

第二个模板参数删除器是一个仿函数,unique_ptr会将里面存放的指针以仿函数的形式传过去,以此来进行自定义处理。

unique_ptr删除器对象不能作为参数传递

通过删除器和类型 + [ ]的使用,我们能处理任何指针类型了,只不过这种使用形式要多记忆一下,容易混淆。

(2)shared_ptr基本使用

unique_ptr的功能几乎完美,唯独缺失了拷贝和赋值的操作。

shared_ptr支持拷贝,采用了引用计数,每当新增一个shared_ptr管理一块空间,就为它计数++,每析构一次就计数--,最后一个析构的释放空间。这类似于最后一个人关灯的操作。

我们可以看见,shared_ptr能够精准避免越界访问的情况。

下面看一看自定义类型如何处理,用法几乎一致,但有区别


#include <iostream>
#include <memory>
using namespace std;

template<class T>
class DeleteArray
{
public:
	void operator()(T* t)
	{
		cout << "DeleteArray" << endl;
		delete[] t;
	}
};

class DeleteFile
{
public:
	void operator()(FILE* f)
	{
		cout << "DeleteFile" << endl;
		if (f)
			fclose(f);
	}
};


class A
{
public:
	~A()
	{
		cout << "~A()" << endl;
	}
};


int main()
{
	shared_ptr<A> sp1(new A[10], DeleteArray<A>());//开辟了一块新的空间
	shared_ptr<A> sp2(sp1);	
	
	shared_ptr<FILE> sp3(fopen("test.txt", "r"), DeleteFile());//开辟了一块新的空间
	shared_ptr<FILE> sp4(sp3);

	shared_ptr<A[]> sp5(new A[5]);//开辟了一块新的空间


	return 0;
}

结果是

shared_ptr删除器对象只能作为函数参数传递

(3)shared_ptr模拟实现

shared_ptr很重要,下面通过其具体实现来加深印象,并且找出shared_ptr的漏洞

这是模拟实现shared_ptr的成员变量,_ptr用于存储开辟的空间,_del用于接收删除器对象,_pcount是用于计数。

在这个模板类中,构造函数的第一个参数用于接收开辟空间的指针对象,如new int返回的int*;第二个参数利用了包装器,用lambda表达式做缺省值(匿名函数对象拷贝出临时对象,被包装器接收),默认以delete来释放空间,我们也可以手动传匹配的函数形式的对象(如仿函数,函数指针,仿函数),它们都能被包装器接收,这也体现包装器的优势。

通过构造和拷贝构造我们就能知道,shared_ptr再调用构造时(开辟新空间)就开辟一个存计数的空间,如果后续调用拷贝构造就会++计数,同理调用析构会--,如果为计数为0就delete开辟的空间。

这里需要理解为什么要用这种方式计数,为什么不用static?

static变量的特点就是一个类就只有一份,这就意味着当我们用同一个类实例化出多个对象时,计数就完全不可控了。假设同一个类有2个对象,4个指针管理,其中3个指针指向其一对象,另一个指针指向另一个对象。但static的计数始终为4,static完全没办法区分这两个对象,所以不可用。

最后还剩下赋值重载,需要注意两点,第一点即瞻前顾后,第二点则是处理自己给自己赋值

下面是所有代码实现,应该很快就能理解了

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr, const function<void(T*)>& del = [](T* t)
			{
				cout << "默认删除器" << endl;
				delete t;
				return;
			})
			:_ptr(ptr)
			, _del(del)
			, _pcount(new int(1))
		{}

		shared_ptr(const shared_ptr& sp)
		{
			(*(sp._pcount))++;
			_ptr = sp._ptr;
			_del = sp._del;
			_pcount = sp._pcount;
		}

		~shared_ptr()
		{
			release();
		}

		void release()
		{
			if (--*(_pcount) == 0)
			{
				_del(_ptr);
				delete _pcount;
			}
		}

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

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

		shared_ptr& operator=(const shared_ptr& sp)
		{
			if (_ptr == sp._ptr)
				return *this;

			release();

			_ptr = sp._ptr;
			_del = sp._del;
			_pcount = sp._pcount;
			(*_pcount)++;

			return *this;
		}

		size_t use_count() const
		{
			return *_pcount;
		}

		T* get() const
		{
			return _ptr;
		}

	private:
		T* _ptr;
		function<void(T*)> _del;
		int* _pcount;
	};

注意const修饰函数可以防止使用该函数时发生权限放大的情况

下面对拷贝操作进行讲解

一定要体会右值的处理,这样才能理解为什么不需要写移动构造。

移动构造的底层就是将有用的指针和无用的指针做交换,将无用的指针析构。如果实现移动构造当然也没问题,不实现是因为析构时有一个条件判断阻止了delete,总体上和值拷贝没任何区别。

(4)循环引用、weak_ptr

看一下下面这段代码,为什么会出现这种状况

class A类似于双向链表,A* _next和A* _prev指向后一个和前一个A。但是由于异常的处理麻烦,我们使用智能指针来包装指针,实现同样的功能并且能自动析构。像上述代码如果觉得难以理解的话,先将它们看作A*,再替换成智能指针,结合operator*和operator->仔细体会。

看懂代码后,我们就能明白sp1和sp2互指

我们可以很好理解sp2和sp1的计数都是2,当析构时,它们都只会计数--,不会delete。最后变成use_count() == 1,这个时候析构操作已经完成了,但空间没有释放,造成了内存泄漏。这就叫循环引用。

处理循环引用,我们需要使用另一种智能指针weak_ptr

下面是一个粗略的实现,帮我们简单看看weak_ptr的结构

weak_ptr不同于其它智能指针,它不支持直接管理资源,它配合解决shared_ptr的一个缺陷,即循环引用导致的泄漏。

weak_ptr不支持RAII,不支持管理资源,也不能用operator*和operator->访问。

在用法上,weak_ptr<int> wp(new int) 这种操作是不可行的(不支持RAII),但是weak_ptr可以用shared_ptr构造和赋值,而shared_ptr可以用weak_ptr构造

当在循环引用出现时使用weak_ptr避免时,使用sp1->_next = sp2就调用了weak_ptr<T>& operator=(const shared_ptr<T>& sp)这个赋值重载。

我们还可以借助make_shared<T>(new T)来让weak_ptr指向数据空间

wp只有在构造的那行才有用,过了之后shared_ptr就析构了(临时对象),这个时候wp就失效了,也叫悬空。这又如何处理?

weak_ptr中use_count()记录了管理的数据有多少次计数,当计数为0时就标记为已失效。expired()就是这个功能,为真时就表示失效

我们还可以在悬空前将数据进行转移,lock()就能实现这项功能

转移前后count计数会++(weak_ptr不会增加计数,转移到shared_ptr,shared_ptr会增加计数),只不过上面的代码是先++,后析构--,整体不变

下面的代码可以说明这一点

我们要把weak_ptr理解为一个单独存储数据的类,不会增加计数。存储的数据很完备,有_ptr、_pcount、_del,但是由于weak_ptr不会--计数,即不会析构,所以当原数据被释放后,就有可能出现悬空的情况。我们可以用expired()检查是否悬空,也可以在悬空前用lock()把数据转移出去。

(5)智能指针总结

C++98推出了auto_ptr(失败的设计,拷贝是管理权转移,拷贝后原来的自动指针为空,调用原来的指针会直接报错),在C++11又推出了unique_ptr、shared_ptr、weak_ptr,用于解决绝大多数内存泄露的场景。当不涉及拷贝时可用unique_ptr,涉及拷贝、传值返回用shared_ptr,weak_ptr用于解决循环引用。

(6)内存泄漏

智能指针要处理的就是内存泄漏问题,即占据着内存却不使用,内存不断被消耗,会导致最终程序被卡死。一般来说,短期快速出现的内存泄漏更容易被发现,而长期运行的慢速内存泄漏的程序影响很大,如服务器几乎不停服,就算每次内存泄漏一点,但时间一长,就会造成服务器崩溃。当今的windows、linux都有各自的内存泄漏检测工具。我们写代码时,如果管理好资源,就能防止内存泄漏。

Ubuntu系统全盘备份——TimeShift的安装与使用 所以如果有那天你不小心作了个大死,并且幸运到还能打开GNOME桌面和Timeshift(只要不是执行sudo rm -f /*这样的自杀程序)的时候,点击你要恢复的那个备份文件再点击Restore,经过短暂的重启后你的系统就成功恢复了。打开软件后,会有一些初始化设置,只需要一直点击 Next。根据自己电脑的内存大小决定是自动备份还是手动备份。当然,对于虚拟机用户来说,没有比在客户机直接拷贝虚拟磁盘文件更棒的方式了。点击左上角的 Create,就会执行备份操作。最下面的意思就是说不会自动备份需要手动操作。 阅读详情

相关推荐

迪文串口屏开发环境搭建全流程:从DGUS Tool到串口联调

串口屏作为嵌入式人机交互的核心组件,通过串口通信实现主控MCU与显示终端的指令交互。其工作原理基于变量地址映射,主控读写VP地址即可驱动屏上控件,兼具低功耗与高稳定性。在工业控制、医疗设备及智能家居面板等场景中,串口屏能显著降低开发门槛与硬件成本。迪文屏凭借DGUS Tool上位机与自定义串口协议,成为主流选择。掌握DGUS Tool版本匹配、串口驱动安装及VP地址规划,是快速搭建开发环境的关键。

weixin_33937499的博客 603

C++智能指针梳理

C++智能指针梳理

chsmiao 5003

水下井口装置及钻杆建模

内容:海上油气资源开采的过程中,通常会由海上钻井平台下入钻杆连接到海底井口装置。OrcaFlex软件可进行各种海洋工程结构动态分析,通过各项数据的输入,能够很方便地进行水下井口装置及钻杆建模。直接使用软件New Line及new vessel功能,再设置模型的各项参数(几何属性、弯曲刚度、轴线刚度、扭转刚度、泊松比等)即可建成井口装置及钻杆模型。 设置管线的几何属性:主要包括管线的内外径、壁厚、...

s0302017的博客 1145

异常和智能指针

异常的优点:1、异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,附带各种你想要的数据,如sql语句。2、 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那 么我们得层层返回错误,最外层才能拿到错误。异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象, 使用基类捕获。异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。

weixin_48133237的博客 795

深入理解C语言内存管理:堆与栈的区别

栈是一种遵循后进先出(LIFO)原则的数据结构。在程序中,栈用于存储局部变量、函数的参数以及函数调用时的返回地址等信息。栈的内存管理由操作系统自动进行,每次函数调用时都会在栈上分配一块内存区域,函数执行完毕后,该内存区域会自动释放。在C语言中,堆与栈是两种不同的内存分配方式,各自有着不同的特点和适用场景。栈内存自动管理,访问速度快,但大小有限,适用于存储局部变量和函数调用信息。堆内存由程序员手动管理,适用于存储动态数据结构,大小不受限制,但访问速度较慢,容易导致内存泄漏等问题。

set_one的博客 1580

90. 将 SAP ABAP 内表内容本地导出成 Excel 文件

这里文件扩展名为 csv,全称是 Comma-Separated Values,意思是这种类型的文件内容,由逗号分隔。除了 ASC 之外,另一个可用的选项是 BIN,适用于 PDF,EXE 等二进制文件的导出。支持海量的输入和输出参数,我们这个简单的本地导出场景里,大部分参数都用不上。这个分隔符的添加逻辑,编写在上图红色高亮矩形框内,使用的语法是。本教程有学习者向我提问,询问如何将 ABAP 某某函数的某某输出参数的值,导出成本地 Excel 文件。把这个内表的内容,下载到本地,以本地文件的方式保存。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1742

C++智能指针常见错误和解决方法,嘿嘿嘿

C++智能指针是一种用于自动管理动态分配的内存的机制,它通过自动删除所指向的对象来防止内存泄漏。C++11标准引入了三种智能指针:`std::unique_ptr`、`std::shared_ptr`和`std::weak_ptr`。以下是这些智能指针的详解、解决思路、解决方法以及代码示例。 ### 1. `std::unique_ptr` #### 解决思路 `std::unique_ptr`用于表示对动态分配对象的独占所有权。在同一时间,只能有一个`unique_ptr`指向某个对象。当`uniq

PythonAigc的博客 948

【部署】Docker部署OpenClaw及常见问题解决(win11)

给AI单独开一个隔离的小容器,AI执行所有命令、下载文件都在这个小盒子里,就算AI被诱导执行危险命令,也不会影响你Win11本机的文件和系统,是必开的安全功能。Docker容器里的OAuth回调,会尝试在你Win11本机的1455端口捕获认证信息,但容器和Win11本机的网络隔离,导致浏览器访问不到这个回调地址。「挂载」就是把你Win11本机的文件夹,共享给容器里的「虚拟小电脑」,两边都能读写文件,比如你想让AI操作你D盘里的代码文件,就需要挂载。

海兰 4697

C++ 中异常机制

5:如果throw 的发生并不在try 区段中,或是没有一个catch 吻合,name系统必选(a)析构所有active local objects (b)从堆栈中将当前的函数推离(c)进入程序堆栈的下一个函数中去,然后重复2~5.throw:在程序的某处发出一个exception,exception可以是默认的类型,也可以是user自定义的类型。C++的exception handling 有三个关键字组成:throw ,catch,try。try:一个区段,将可能引起exception的部分包含起来。

cwei231的博客 1004

BMS硬件设计要点:从STM32主控到BQ76920监控芯片的工程实践

电池管理系统(BMS)是锂电安全与寿命保障的核心,其硬件设计涉及微控制器选型、专用监控芯片接口、电源完整性、信号完整性及PCB布局等多维度协同。基于STM32与BQ76920的典型双层架构,需深入理解功能安全解耦原理——关键保护由ASIC硬件实现,算法决策交由MCU完成,契合ASIL-B等级要求。技术价值体现在毫秒级硬件保护响应、±10mV电压采样精度及抗EMI鲁棒性设计;广泛应用于电动工具、储能系统与轻型电动车等中高功率场景。本文聚焦BMS控制板硬件实现中的高频时钟EMC、I²C总线信号完整性、被动均衡可

weixin_30591519的博客 92

汽车电子RTOS--AUTOSAR OS

AUTOSAR OS(AUTomotive Open System ARchitecture Operating System)是AUTOSAR(汽车开放系统架构)标准中的一个重要组件,它是一个专门为汽车电子控制单元(ECU)设计的实时操作系统(RTOS)。AUTOSAR OS旨在提供一个标准化的软件平台,以促进不同供应商和汽车制造商之间的组件复用和协同开发,提高软件质量和降低成本。

MHD0815的博客 1798

C++智能指针的使用

C++智能指针的使用unique_ptr功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 C++11中提供了三种智能指针...

首飞的博客 1798

25、BDS-3卫星信号性能与PPP-B2b增强消息完整性分析

本文对BDS-3卫星信号性能及PPP-B2b增强消息的完整性进行了系统分析。评估了MEO、IGSO和GEO卫星的轨道与时钟精度,得出BDS-3总体SISRE为0.25 m,系统完整性可覆盖99.999%的误差。分析表明SIS中断主要由时钟调整和轨道机动引起,个别卫星存在因境外监测受限导致的非计划中断。通过自研解码软件对PPP-B2b增强消息进行采集与解析,实验结果显示BDS-3和GPS的平均历元完整性率均高于98.55%,其中卫星掩码和时钟校正消息的完整性率超过99.8%。研究还发现IGSO卫星相比MEO卫

quiet的专栏 145

C/C++知识点总结

C++笔试知识点总结(语法及常用函数)输入输出C++中的new/deletevector 输入输出 (1) 第一行输入两个整数M,N;接下来M行分别输入2个数,最后一行输入N个数(由于输入行数,个数都确定,用循环很好处理) (2)循环输入多行 while(cin>>m>>n){ } (3)输入一行数字,数字间以空格分隔 #include<sstream> in.........

LilyZJ的博客 1429

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

C++11:异常与智能指针

实际中很多公司都会自定义自己的异常体系进行规范的异常管理。公司中的项目一般会进行模块划分,让不同的程序员或小组完成不同的模块,如果不对抛异常这件事进行规范,那么负责最外层捕获异常的程序员就非常难受了,因为他需要捕获大家抛出的各种类型的异常对象。因此实际中都会定义一套继承的规范体系,先定义一个最基础的异常类,所有人抛出的异常对象都必须是继承于该异常类的派生类对象,因为异常语法规定可以用基类捕获抛出的派生类对象,因此最外层就只需捕获基类就行了。

2302_79054145的博客 1437

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

C++中的异常和智能指针

本文介绍了C++中的异常以及配合异常而生的智能指针,通过对比C语言中的错误码,总结了异常的优点;又通过分析异常的抛出-匹配规则,归纳了异常的缺点;然后针对缺点提出了简单的智能指针处理方案,接下来介绍了C++11库中的auto_ptr,unique_ptr,shared_ptr三种不同的智能指针,并介绍了内存泄露

tianxhiliangtadi的博客 814

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

C++『异常』

异常处理在软件开发中扮演着关键的角色,它为程序员提供了一种有力的手段来处理和响应程序执行过程中可能出现的错误。本文将深入探讨异常的基本概念、异常处理方式、异常的使用技巧和异常体系的设计,以帮助开发者更好地理解和应用异常处理机制

北 海的博客 2425

【C/C++】内存管理详解:从new/delete到智能指针的全面解析

智能指针是一种封装了普通指针的类,通过自动管理内存的分配和释放,简化了内存管理的过程。它们利用独占或共享所有权的概念,确保在对象不再使用时,自动释放相关资源,从而提高代码的安全性和可维护性。自动内存管理:避免手动调用delete或free,减少内存泄漏的风险。异常安全:在异常发生时,智能指针能够确保资源被正确释放。所有权管理:通过不同类型的智能指针,管理资源的独占或共享所有权,提高代码的表达力和安全性。掌握内存管理不仅是C/C++开发者的必备技能,更是提升编程能力的重要一步。

万物皆有灵 1322

【C++】异常

C语言是如何处理错误的?C语言处理错误的机制实际上比较低级,比如直接终止程序assert或者返回错误码自行查表找错等,那C++作为一个面向对象的语言,对比C语言来说处理错误的方式有何优点呢?今天让我们一起来学习一下『 异常处理』吧。

℉f的博客 2704

异常与智能指针

我们的想法是:既然是由于A节点里的next或pre指针指向另一个节点B时,调用了share_ptr的赋值拷贝函数后,使得B节点的计数增加了,最后在循环中导致内存无法释放,那我们只需要控制若是next或者pre指针指向节点B时,节点B的引用计数不增加,最后资源不就正常释放了吗,因此我们对pre或者next进行包装另一个类,使其可以正常指向节点,同时在复制拷贝中不增加计数。版本的智能指针,其支持指针的拷贝,原理也较为简单,就是每一份资源都用一个引用计数记录资源的拥有者的个数,只有当引用计数为0时才释放资源。

m0_74494481的博客 1100

【C++】异常+智能指针+特殊类和类型转换

处理错误的异常体系,智能指针unique_ptr,shared_ptr,引用计数,线程安全,循环引用,weak_ptr,定制删除器,常见特殊类,单例模式,C++的四种类型转换…

The porters of knowledge 948

C/C++面试

A:这样可以提高编译效率,因为分开的话,这个类只需要编译一次生成对应的目标文件,以后在其他地方用到这个类时,编译器查找到了头文件和目标文件,就不会再次编译这个类,从而大大提高了效率。A:常量在C++里使用const关键字定义,常量定义必须初始化。对于局部对象,常量存放在栈区;对于全局对象,编译期一般不分配内存,放在符号表中以提高访问效率;对于字面值常量,存放在常量存储区。A:左值:能对表达式取地址的具名对象/变量等。一般指表达式结束后依然存在的持久对象。

D_YM00的博客 2270

C++-智能指针详解

引言 除了静态内存和栈内存外,每个程序还有一个内存池,这部分内存被称为自由空间或者堆。程序用堆来存储动态分配的对象即那些在程序运行时分配的对象,当动态对象不再使用时,我们的代码必须显式的销毁它们。 在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联...

魔筝炼药师 8577

C/C++后端开发面经(2)——C/C++相关

C/C++后端开发面经(1)——C/C++

weixin_43687811的博客 1623

C++进阶:智能指针

其除了具备智能指针管理资源与指针操作的功能外,对智能指针的拷贝也做了相关的实现,其设计思路为拷贝后,将指针转移,将原有指针悬空。weak_ptr只做链接功能,因为weak_ptr与shared_ptr不是一个类型的智能指针,weak_ptr想要从shared_ptr获取资源只有两个方式,一是被声明为友元,二是为shared_ptr添加get接口,get接口必须使用const修饰this指针。自定义一个双向链表的节点,链表的每个节点都是动态开辟而出的,这里我们采用智能指针的方式去定义与管理。

qq_71806743的博客 1334

c++中的智能指针

weak_ptr 指向一个由 shared_ptr 管理的对象,但不会影响所指对象的生命周期,也就是将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数。unique_ptr是C++11标准库提供的一种智能指针,它能够自动管理动态分配的内存,从而避免了内存泄漏的问题,unique_ptr是独占式的,即完全拥有它所管理对象的所有权,不和其它的对象共享,unique_ptr禁用了拷贝构造和拷贝赋值构造,仅仅实现了移动构造和移动赋值构造,这也就使得它是独占式的。

k5426244的博客 1333
上一篇: C/C++相关概念和易错知识点(54)(lambda、function、bind)
下一篇: C/C++相关概念和易错知识点(56)(特殊类的设计、new和delete底层调用分析)
-SGlow-
博客等级 码龄3年 1513粉丝 118原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值