StupidBeauty
??Read times:509????Posted at:Sun Jan 8 07:23:27 2017?? - no title specified

std::bad_alloc异常的另一个可能原因:野指针

Table of Contents

1概述

2细节

2.1结构

2.1.1GodLogic

2.1.2Watcher

2.1.3Actor

2.2原理

2.2.1正常流程

2.2.2出现问题的地方

2.3解决及改进

2.3.1简单解决

2.3.2进一步解决

2.3.3根本解决

1 概述

std::bad_alloc异常的标准意义是指调用new时内存分配失败,准确地说指的是任何情况下的内存分配失败,不一定系统中的内存真的就用完了。

另一方面,未初始化的指针,会引起未定义的行为。未定义的行为中,也包含着“进行不正确的内存分配”这种行为。

因此,野指针本身也可能成为std::bad_alloc的原因的。

这几天就遇到过由野指针引起的std::bad_alloc,经过研究之后,做了一些精简,整理到一个最简化的展示项目中。

项目地址在这里:

https://bitbucket.org/hxcan/invadingsalesperson/src/eda0cd46721f/stdbadalloc/?at=default

编译运行之后,等待5秒钟,会出现以下输出:

Starting /Ada/SoftwareDevelop/invadingsalesperson/build-stdbadalloc/stdbadalloc...

/Ada/SoftwareDevelop/invadingsalesperson/stdbadalloc/GodLogic.cpp, 36, GodLogic, this: 0x7fffa9906b10

/Ada/SoftwareDevelop/invadingsalesperson/stdbadalloc/Watcher.cpp, 23, sendEmergencyEventAlarm, logic:0x7fd89e87ebc8

/Ada/SoftwareDevelop/invadingsalesperson/stdbadalloc/GodLogic.cpp, 18, processEmergencyEvent, this: 0x7fd89e87ebc8

/Ada/SoftwareDevelop/invadingsalesperson/stdbadalloc/Actor.cpp, 17, reactToEmergencyEvent, logic:0x7fd89e87eba8

/Ada/SoftwareDevelop/invadingsalesperson/stdbadalloc/Actor.cpp, 29, reactToEmergencyEvent, exception: std::bad_alloc

/Ada/SoftwareDevelop/invadingsalesperson/build-stdbadalloc/stdbadalloc exited with code 0

2 细节

2.1 结构

这个项目的代码主要由三个类组成:

2.1.1 GodLogic

这是项目中主要逻辑的实现类。它会指使Watcher来监视某些事件,指使Actor来做出某些回应,并且维护一些共享的数据。

2.1.2 Watcher

这在项目中起到“监视”的作用,它会监视某些事件,并且在发生事件时对GodLogic进行回调,以实现事件的报告。

2.1.3 Actor

这在项目中起到“响应”的作用,在被GodLogic调用时,会做出某些动作。它会通过回调向GodLogic查询某些共享数据。

2.2 原理

2.2.1 正常流程

在这个示例项目中,我会让Watcher产生一个假造的事件,这会导致它对GodLogic进行回调;

GodLogic接下来调用Actor对事件做出响应;

Actor在做出响应的过程中需要再回调GodLogic的函数来查询某些共享数据。

2.2.2 出现问题的地方

本来,以上流程是可以正常工作的,三个模块互相之间以指针引用。

而在实开发过程中,出现了一个小错误,Watcher在被构造后,并未被通过接口告知GodLogic对象的指针,这使得Watcher类中的logic指针一直处于未初始化状态。Watcher.h第13行。

Watcher类实例在发生事件时,通过这个未初始化的logic指针调用GodLogic类实例的processEmergencyEvent方法。Watcher.cpp第25行。

这个实际不存在的GodLogic类实例的processEmergencyEvent方法中,调用Actor类实例的 reactToEmergencyEvent方法。GodLogic.cpp第23行。

此时,被调用的这个Actor类实例实际上也是不存在的,在它的reactToEmergencyEvent方法中,又回调它所认为的GodLogic类实例的getSharedInformation方法,以查询某个共享数据。Actor.cpp第23行。

被回调的这个GodLogic类实例自然也是不存在的,虽然它的getSharedInformation方法本身狠简单,只是返回sharedInformation成员对象的值。位于GodLogic.cpp第6行。但是,由于这个实例本身相当于是内存中一块随机的区域,它的生命周期本身就不对,它实际上就是个不存在的对象。所以,在这个方法返回的过程中,对string类型进行赋值时的内存分配过程失败了。因而抛出std::bad_alloc异常。被Actor.cpp第27行捕获到异常。

2.3 解决及改进

2.3.1 简单解决

根本原因就在于,在创建Watcher类实例之后,未向它指定GodLogic类实例的指针。针对这个简单项目展示的具体情况,在GodLogic.cpp第38行,加上一句watcher->setLogic(this)进行赋值即可解决问题。

2.3.2 进一步解决

由于这种情况本身调试起来也不容易发现,因此,在最初写代码时,就要耐心地做好一些对于未来可能坑到自己的问题的预防工作,例如此处的Watcher类的定义中,logic成员变量的声明位于Watcher.h的第13行,在写这行代码时可以多花个几秒钟为这个成员变量写个默认值。GodLogic * logic=nullptr;。这样,在实际运行过程中,早早的就会在执行到Watcher.cpp第25行就出错,因为它所调用的是一个GodLogic*类型的空指针,这样就会一步定位到问题。调试起来也容易发现得多。赶工期时往往有意忽略一些细节,导致犯下这些因小失大的错误。

2.3.3 根本解决

既然GodLogic实例的指针对于Watcher类来说是必不可少的,那么实际上就应当在构造函数中就指定GodLogic类实例的指针,同时禁用掉默认的构造函数。

李晓阳