最近工作上发现一个比较复杂的代码出现随机报错问题,话不多说,直接debug模式开启ASAN机制构建程序,
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -fstack-protector -fsanitize=address -fno-omit-frame-pointer")
很快能定位到报错的行,调用某个对象的成员函数的内部报错,说某个成员锁对象有问题,非法读取内存之类的。然后围绕这个锁,看了相关代码,没发现问题。干脆屏蔽这个锁的访问,发现访问某些成员变量也开始报错了,很奇怪,第一感觉是如果对象本身为空,外部调用的时候就应该报错了阿。
实在是想不通,看了一圈代码,也没发现明显问题,难道是ASAN此时报错较晚?干脆做个实验吧。代码如下:
#include <memory>
class Person {
public:
Person(const std::string& name) : name(name) {
std::cout << "Construct Person: " << name << std::endl;
}
~Person() {
std::cout << "Destroy Person: " << name << std::endl;
}
void print() {
printf("xx\n");
std::cout << "Name: " << name << std::endl;
}
private:
std::string name;
};
int ptrTest() {
// 使用make_shared创建shared_ptr实例
auto p1 = std::make_shared<Person>("John");
// 直接创建shared_ptr
std::shared_ptr<Person> p2 = std::make_shared<Person>("Mary");
// 使用get()获取原始指针
Person* rawPtr = p1.get();
// 使用重载的->调用成员函数
p1->print();
// shared_ptr支持 copy
std::shared_ptr<Person> p3;// = p1;
if(p3) {
p3->print();
}
// 当所有shared_ptr都离开作用域或被重置时,对象才会被销毁
return 0;
}
int main() {
ptrTest();
return 0;
}
果不其然,p3不赋值的情况下,debug模式下也能进入print内部进行调用,并不报错,如果print内部访问成员变量,那么此时报错,说读取非法内存,asan此时才报错,如果print内部完全不访问任何成员变量,那么整个过程可以顺利进行!即使是Debug模式。
那么最简单的解决方案就是调用智能指针的成员之前,就先检查指针本身是否合法了。直接if判断就行。最根本的解决方法是找到为什么没被赋值,这是必须要解决的问题了。