技术标签: # More Effective C++ c++
class Animal {
public:
virtual void processAdoption() = 0;
}
class Cat : public Animal {
public:
virtual void processAdoption();
}
class Dog : public Animal {
public:
virtual void processAdoption();
}
void batchAdoption(istream& dataSource) {
while (dataSource) {
Animal *animal = readAnimal(dataSource); // 可能抛出异常
animal->processAdoption(); // 可能抛出异常
delete animal;
}
}
// 在上面的batchAdoption()方法中
// readAnimal()和 processAdoption() 都可能抛出异常
// 程序中断,从而导致delete animal无法执行,内存泄漏发生。
void batchAdoption(istream& dataSource) {
while (dataSource) {
Animal *animal = readAnimal(dataSource); // 不能放入try中,否则animal对外部不可见
try {
animal->processAdoption();
} catch (...) {
delete animal; // 代码冗余
throw;
}
delete animal; // 代码冗余
}
}
void batchAdoption(istream& dataSource) {
while (dataSource) {
auto_ptr animal(readAnimal(dataSource));
animal->processAdoption();
// 无需调用语句delete animal,出了作用域即调用析构函数
}
}
new A
,再 new B
,然而在 new B
的过程中出现异常,此时 new A
指向的内存就会出现内存泄漏。BookEntry::BookEntry(const string &name, const string& address,
const string& image, const string& audio)
: m_name(name), // name 是非指针变量
m_address(address), // address 是非指针变量
m_image(createImage(image)), // image 和 audio 均是常量(const)指针
m_audio(createAudio(audio)) {
}
Image* createImage(const std::string& image) {
// image 在 audio 面前初始化,所以不需要捕捉异常
if(image != "") return new Image(image);
else return 0;
}
Audio* createAudio(const std::string& audio) {
// audio 第二个初始化,所以捕捉异常到异常后需要释放 image 对象
try {
if(audio != "") return new Audio(audio);
else return 0;
} catch () {
delete m_image; // 捕捉到异常需要释放已经初始化的 image 对象
throw;
}
}
// 可以看到针对m_image变量定义了构建函数
// 在heap中创建变量的工作放到这个构建函数中,并返回创建好的指针
image
指针和 audio
指针改为被智能指针包裹即可),利用作用域和生命周期来控制具体的行为。~Destructor() {
try {
doSomething();
} catch (...) {
// doNothing, avoid more exception
}
}
// 示例1:抛出的异常为局部变量
void passThrowWidget() {
Widget widget;
doSomething(widget);
// 抛出的对象是widget的一个副本
// 当前作用域的widget在离开本函数时已经被销毁
throw widget;
}
// 示例2:抛出的异常为静态局部变量
void passThrowWidget() {
static Widget widget;
doSomething(widget);
// 尽管本函数内的widget不会被销毁,但是抛出的widget依然是一个副本
throw widget;
}
// 无论原对象以什么形式定义,抛出的对象总是一个副本。
// 这样做保证了,catch捕获的对象总能存在,
// 否则可能导致捕获的异常对象已经被销毁。
class Widget; // 基类
class ChildWidget : public Widget {
// 派生类
}
void passThrowWidget() {
ChildWidget child; // ChildWidget 是 child 的类型
Widget &widget = child; // Widget 是 widget 的静态类型
throw widget; // 调用 Widget 的复制构造函数进行复制,而不是 ChildWidget
}
catch (Widget& w) // 方案一:不复制异常,而是直接抛出当前的异常
{
// ...
throw; // 重新抛出当前的异常,不管 w 的动态类型是什么,最后都可以得到保证
}
catch (Widget& w) // 方案二:复制后抛出
{
// ...
throw w; // 抛出当前的异常的副本,相当于新的异常,且副本只保留了原对象静态类型
}
// 方案二带来的问题是:
// 复制操作带来的开销
// 复制行为是基于静态类型的拷贝,因此传递抛出的对象可能不是原来想要传递的对象
catch (Widget w)
【by value 方式捕捉】catch (Widget &w)
【by reference 方式捕捉】catch (const Widget &w)
【by reference-to-const 方式捕捉】int
转为 double
;而异常捕获的参数是不允许前述的基本隐式转换,即对 int
异常的抛出不会被捕获 double
异常的 catch 语句捕获到。catch (const void*)
可以捕捉任意指针类型的异常】try{
}
catch (base& ex){
// ...
}
catch (derived& ex){
// 这个语句永远不会被执行,因为所有针对继承类的异常都被前面的语句捕获了
// ...
} // 要想该语句被执行,只能将该语句移到 catch(base& ex) 的前面去
void func() {
Widget error; // 形式1
static Widget errorStatic; // 形式2
Widget *errorHeap = new Widget; // 形式3
throw &error; // 形式1
throw &errorStatic; // 形式2
throw errorHeap; // 形式3
}
catch (Widget *widget) {
// ...
}
// 问题一的体现:
void func() {
Widget widget;
throw widget; // 第一次复制
}
catch (Widget widget) {
// 第二次复制
}
// 问题二的体现:
class exception {
public:
virtual const char *what() throw(); // ”throw()“关键字声明该函数不会抛出任何异常
}
class DerivedException : public exception {
public:
virtual const char *what() throw(); // 虚函数实现多态
}
void func() {
DerivedException widget;
throw widget; // 的确是抛出了派生类的异常,但是捕获函数中会将其切割为基类,随后就调用了基类的 what 函数
}
catch (exception widget) {
// 捕捉继承体系里的所有异常
widget.what(); // 调用的是exception::what(),这种情况叫做slicing(切割),即子类信息被切割掉,只留下基类的信息
}
void func() {
DerivedException widget;
throw widget;
}
catch (exception& widget) {
widget.what(); // 调用的是DerivedException的what,实现了多态
}
throw()
,则表示该函数不抛出任何异常。void f2() throw(int);
表示 f2
只抛出类型为 int
的异常。unexpected() -> terminate() -> abort()
,因此程序如果违反异常生命,缺省结果就是程序被中止。template<class T>
bool operator==(const T& left, const T& right) throw() {
// 这样是一种不好的做法
return &left == &right;
}
// 我们无法确定,取地址操作符“&”是否已经被重载,且可能抛出异常。
// 此种情况的实质是,我们无法确定,所有类对象的同名函数都不会抛出异常。
throw()
进行修饰:如果函数 A 内调用了函数 B,而函数 B 无 exception specification,那么 A 函数本身也不要设定 exception specification。【内部允许产生所有异常,外部自然也不要加以限制】
typedef void (*CallbackPtr)();
class Callback {
public:
Callback(CallbackPtr func) : m_func(func) {
}
void makeCallBack() throw() {
// 这里的异常声明很容易带来问题
m_func(); // 可能抛出异常
}
private:
CallbackPtr m_func;
}
// 如代码所示,如果注册的“回调函数”没有throw修饰,
// 而调用“回调函数”的外层函数却有throw修饰,
// “回调函数”抛出异常就会引起程序终止。
set_unexpected()
,可以向该函数中传递我们自定义的函数,来替换默认的 unexpected()
。首先,安装一个稳定的pip版本:python -m pip install pip==20.0.1 -i https://pypi.tuna.tsinghua.edu.cn/simple/其次,再安装jupyter notebookpip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyter最后,在vscode安装jupyter使用方法:新建一个ipynb文件即可快捷键两个必记:shift+enter表示增加一个cellc_vscode shift+enter进入juypter
1.首先,系统的初始化,初始化SCI时钟功能void InitPeripheralClocks(void){ EALLOW;// HISPCP/LOSPCP prescale register settings, normally it will be set to default values SysCtrlRegs.HISPCP.all = 0x0001; SysCtrlRegs.LOSPCP.all = 0x0002; SysCtrlRegs.PCLKCR0.bi_串口fifo接收数据
垂直拆分就是要把表按模块划分到不同数据库表中(当然原则还是不破坏第三范式),这种拆分在大型网站的演变过程中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一起,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分成多个弱耦合的服务_垂直划分和水平划分
Node 是一个让 JavaScript 运行在服务端的开发平台因为业务需求,想在自己的后台中部署 node.js 服务在此整理一下,在 Linux 系统下的 Node.js 安装指导,希望能帮到各位有需求的小伙伴..._node.js安装教程linux
转载自:《du命令》-linux命令五分钟系列之三du(disk usage),顾名思义,查看目录/文件占用空间大小#查看当前目录下的所有目录以及子目录的大小$ du -h $ du -ah#-h:用K、M、G的人性化形式显示#-a:显示目录和文件 du -h tmp du -ah tmp#只查看当前目录下的tmp目录(包含子目录)的大小#查看当前目录...
原文https://www.cnblogs.com/zishi/p/6780719.html原文链接:http://www.cnblogs.com/zishi/p/6780719.html在实际项目中写单元测试的过程中我们会发现需要测试的类有很多依赖,这些依赖项又会有依赖,导致在单元测试代码里几乎无法完成构建,尤其是当依赖项尚未构建完成时会导致单元测试无法进行。为了解决这类问题我们引入了M..._mockito怎么mock service中工具类的行为
UWB定位 - DWM1000模块调试简单心得 - 1 上一篇搭建了下软硬件的基础环境,这篇开始记录调试需要注意的地方或者”坑”。先以一基站一标签来进行。1、将我们的模块连接后上电。注意这里DWM1000模块(也就是stm32开发板)一定要使用独立电源(5v / 1.5A ↑↑↑)单独供电,如果你用USB to TTL或者电脑的USB接口给stm32开发板供电(dwm10003.3V与stm3..._读取dwm1000 一直是0
控件名简写控件名简写Web 窗体LabellblTextBoxtbButtonbtnLinkButtonlbHyperLinkhlRepeatorrpt_控件命名规范
上次介绍了磁盘,这篇来介绍一下RAID要介绍RAID技术的原因,其实是因为目前大部分分布式存储在做的事情其实RAID在很多年前就已经做到了,所以如果你希望做存储相关的事情,那么RAID是必须要理解,但不一定要用到的概念:)计算机要存储和读取数据,主要依托这么两个部件:1.通信管道和通信协议,心灵感应还需要靠电波通信呢不是?~一般来说这通信管道在计算机内就是总线,使用电信号,在...
在用Android MediaCodec编码h264的时候,会遇到,dequeueOutputBuffer在成功获取到config帧(sps pps)及第一个I帧后,dequeueOutputBuffer然后结果一直为-1的情况, 在三星note3及小米3,小米6都是这样的,解决方案如下:关键在下面这个函数的第四个参数上,就是时间戳,这个参数一定要填写,可以类似的这么简单的算一下:..._海思 dequeueoutputbuffer -1
首先,到opencv官网上面下载opencv的安装包。由于我实在Windows环境下进行的配置,所以说下载win pack。下载完之后双击运行,出现下图所示窗口。找一个吉利的路径把他存放好(最好不要是中文路径),而后点击Extract。等待读条,读条完毕后他会生成两个文件夹和几个txt文件,如下图所示。第二步,到cmake官网上下载并安装cmake。然后打开bin文件夹下的...
uClinux的通用c库:uC-libc和uClibc的区别概述uClinux通常使用两种c库:uC-libc和uClibc.尽管它们名字近似,但有很大区别.本文是对它们不同点的快速浏览.uC-libc是uClinux的原始c 库,它基于Linux-8086 c库,该c 库是ELKs 工程的一部分,支持m68000结构.uC-libc是一个相当全面的c 库,但它的一些API是非标准的,一些通用