pure virtual method called错误定位

项目场景:

代码集成三方库后,在程序即将退出时,报错崩溃,程序没有正常退出。提示如下错误: pure virtual method called terminate called without an active exception

使用gdb调试,查看程序堆栈
在这里插入图片描述


问题描述:

问题并不是必现,大多数情况下都能正常执行完,且异常基本都出现在程序即将退出时。崩溃时打印pure virtual method called
字面翻译是:纯虚函数被调用。


原因分析:

奔溃时的打印信息是定位问题的入口。 首先确定pure virtual method called这句话是哪里打印的,虚函数是C++的内容 ,且从gdb的奔溃信息看,是libstdc++.so库中的内容,将这段打印信息在gcc中查找 在libstdc++-v3/libsupc++/pure.cc +49

在这里插入图片描述
继续查找__cxa_pure_virtual函数,在gcc/cp/decl.c +4405中
在这里插入图片描述
最终在gcc/cp/class.c +9277,看到这样的描述,程序在执行时会给纯虚函数赋值,这个值就是__cxa_pure_virtual。
在这里插入图片描述

当你构造一个派生类的实例时,具体发生了什么?如果类包含虚函数表,过程会像下面这样:
第一步:构造最顶层的基类部分
a、让实例指向基类的虚函数表
b、构造基类实例成员变量
c、执行基类构造函数
第二步:构造派生部分(递归的)
a、让实例指向派生类的虚函数表
b、构造派生类实例成员变量
c、执行派生类构造函数
析构时则是按相反的顺序,就像这样:
第一步:析构派生部分(递归的)
a、(实例已经指向派生类的虚函数表)
b、执行派生类析构函数
c、析构派生类实例成员变量
第二步:析构基类部分(递归的)
a、让实例指向基类的虚函数表
b、执行基类析构函数
c、析构基类实例成员变量

#include <iostream>
#include <unistd.h>
#include <stdio.h>
using namespace std;

class Base
{
public:
    Base()
    {
        printf("base constructor virtual pointer %p\n", (void*)*(int64_t*)((void*)this));
    }

     ~Base()
    {
        printf("base destructor virtual pointer %p\n", (void*)*(int64_t*)((void*)this));
    }

    virtual void doIt() const = 0;
};

class Derived : public Base
{
public:
    Derived()
    {
        printf("derived constructor virtual pointer %p\n", (void*)*(int64_t*)((void*)this));
    }

    void doIt()const {return ;};
    ~Derived()
    {
        printf("derived destructor virtual pointer %p\n", (void*)*(int64_t*)((void*)this));
    }
};

int main()
{
    Derived* const base = new Derived();
    delete base;
}

执行结果
在这里插入图片描述

出现该问题
1、在基类的构造函数里直接调用虚函数
2、在基类的析构函数里直接调用虚函数
3、 在基类的构造函数里间接调用虚函数
4、在基类的析构函数里间接调用虚函数
5、使用野指针调用虚函数
通过排查代码,确认函数中没有出现前四种状况。
大概率是情况5,野指针的使用。

当前程序是 linux运行,程序大致逻辑是开辟线程,线程内部一直对资源进行访问,程序执行后没有对线程进行销毁 ,导致线程内部对已经释放的资源进行访问。


解决方案:

优雅的退出线程。

    #include<stdio.h>  
    #include<stdlib.h>  
    #include <pthread.h>  
    void *thread_fun(void *arg)  
    {  
        int i=1;  
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        /*同步取消,等到下一个取消点再取消*/
//      pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
        /*异步取消, 线程接到取消信号后,立即退出*/
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

        printf("thread start \n");  

        while(1)  
        {  
            i++;  
        }  
        return (void *)0;  
    }  
    int main()  
    {  
        void *ret=NULL;  
        int iret=0;  
        pthread_t tid;  
        pthread_create(&tid,NULL,thread_fun,NULL);  
        sleep(1);  

        pthread_cancel(tid);//取消线程  
        pthread_join(tid, &ret);  
        printf("thread 3 exit code %d\n", (int)ret);  
		/*do something to clean*/
        return 0;  
    }  

设置为异步取消,在pthread_join后删除资源。

也可设置同步取消

    #include<stdio.h>  
    #include<stdlib.h>  
    #include <pthread.h>  
    void cleanup(void*)
    {
    	printf("clean\n");
	}
    void *thread_fun(void *arg)  
    {  
        int i=1;  
         pthread_cleanup_push(cleanup,NULL);  //释放线程内的内存,可以防止内存泄漏
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        /*同步取消,等到下一个取消点再取消*/
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);   

        printf("thread start \n");  
        while(1)  
        {  
            i++;  
            // printf("aaa\n");   //可作取消点    凡有阻塞作有的POSIX C函数都会有取消点, (具体包括哪些,可以查看man 7 pthreads)
	        pthread_testcancel();  //手动建立一个取消点, 防止线程中无取消点,导致线程不取消  //如果屏蔽所有的取消点.主程序就会堵在pthread_join里.
	        // printf("bbb\n");//可作取消点
	        // printf("ccc\n");//可作取消点
	        // sleep(5);//可作取消点
	        // printf("ddd\n");//可作取消点
	        // pthread_testcancel();//可作取消点
	        // system("ls");//可作取消点

        }  
        return (void *)0;  
    }  
    int main()  
    {  
        void *ret=NULL;  
        int iret=0;  
        pthread_t tid;  
        pthread_create(&tid,NULL,thread_fun,NULL);  
        sleep(1);  

        pthread_cancel(tid);//取消线程  
        pthread_join(tid, &ret);  
        printf("thread 3 exit code %d\n", (int)ret);  

        return 0;  

    }  

同步取消可在clear函数中清除数据

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值