shared_ptr上的新放置在删除时使seg错误 [英] placement new on shared_ptr make seg fault when delete

查看:119
本文介绍了shared_ptr上的新放置在删除时使seg错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑解决方案::

事实上,我只是忘记了复制构造函数中的新位置> <

In fact, i juste forget the placment new in the copy constructor ><"

问题:

我有一个怪异的问题。尝试了很长的momnet起源后,我发现masi无法理解。
如果有人可以解释

I have a weird problem. After having tried for a long momnet origin I found masi does not understand. If someone can explain to me why.

我的班级

class B; //on other file
class A {
   public:
     A(int type) : type(type)
     {
        switch(type)
        {
           case TOKEN:
           {
             for(int i=0;i<4;++i)
                new(&token.h[i].link) shared_ptr<B>; //< init the ptr on the addr (because of union)
           }break;
           case OTHER: {}break;
        }
     }
     ~A()
      {
        switch(type)
        {
            case TOKEN:
            {
             for(int i=0;i<4;++i)
             {
                /*option 1*/ token.h[i].link.~shared_pt<B>(); //< Make seg fault
               /*option 2*/ token.h[i].link.reset(); //< ok
             }
            }break;
            case OTHER: {}break;
         }
        }
      }
   enum {TOKEN=0,OTHER} type;

   union {
       struct {
           double score;
           struct {
               std::shared_ptr<B> link;
               double to_find;
               } h [4];
       }token;

       struct {
          //else
       } other;
   }
};

我的代码:

void f()
{
    vector<A> vec;
    A tmp = A(A::TOKEN);
    vec.emplace_back(tmp);
}

选项1:离开f时会导致错误;
选项2:好的,但是〜shared_ptr()没有调用,所以会导致内存泄漏,对吧?

Option 1: this causes an error when leaving f; option 2: Ok but ~shared_ptr() is not call, so it make memory leak, right?

如果您有一个可以帮助我理解的想法谁错了。

If you have an idea that could help me understand who is wrong.

编辑:
我在Ubuntu 12.04x86上使用C ++ 11和gcc.4.6.3。

I use C++11 with gcc.4.6.3 on Ubuntu 12.04x86.

原始代码:

    class stack_token {
        public:
            stack_token();
            stack_token(const stack_token& other);
            stack_token(const int i,Parser::peptide::peak* data); //peak
            stack_token(const int i,const double e,AnalyseurPeptide::stack_token* peak); //aa
            stack_token(const int i); //aa pour boucher un trou
            stack_token(const double score); //HEADER

            ~stack_token();

            stack_token& operator=(const stack_token& other);

            inline stack_token* get_peak_stack_NULL() {
                stack_token* res = aa_token.pt_data;
                aa_token.pt_data=NULL;
                return res;
            };

            void __print__() const;


            enum Type {UNKNOW=-1,AA_TOKEN=0,AA_HOLD_TOKEN,/*AA_LIST,*/PEAK_TOKEN, HEADER_TOKEN} type;

            union {
                struct  {
                    int index;
                    double error;
                    stack_token* pt_data;
                } aa_token;

                struct{
                    double error;
                    stack_token* pt_data;
                    std::vector<int> aa_index;
                } aa_hold_token;

                struct {
                    int index;
                    Parser::peptide::peak* pt_data;
                } peak_token;

                struct {
                    double score;
                    struct {
                        std::shared_ptr<std::list<list_arg> > link;
                        double to_find;
                    } holds [Parser::peptide::SIZE];
                } header_token;
            };
    };

 stack_token::~stack_token()
{
switch(type)
{
    case AA_TOKEN:
    {
       if(aa_token.pt_data != NULL)
            delete aa_token.pt_data;
    }break;

    case AA_HOLD_TOKEN :
    {
        aa_hold_token.aa_index.~vector<int>();
    }break;

    case PEAK_TOKEN : 
    {
    }break;

    case HEADER_TOKEN : 
    {
       for (int i=0;i<Parser::peptide::SIZE;++i)
            header_token.holds[i].link.reset();//~shared_ptr<std::list<list_arg> >();
    }break;

    default : break;
}
};


  stack_token::stack_token()
{
this->type = UNKNOW;
};

stack_token::stack_token(const int i,Parser::peptide::peak* data) //peak
{
this->type=PEAK_TOKEN;
peak_token.index = i;
peak_token.pt_data = data;
};

stack_token::stack_token(const int i,const double e,AnalyseurPeptide::stack_token* peak) //aa
{
this->type=AA_TOKEN;
aa_token.error =e;
aa_token.index = i;
aa_token.pt_data = peak;
};

stack_token::stack_token(const int i)
{
this->type=AA_HOLD_TOKEN;
aa_hold_token.error = 0;
aa_hold_token.pt_data = this;
new(&aa_hold_token.aa_index) vector<int>();
};


stack_token::stack_token(const double score) //HEADER
{
this->type = HEADER_TOKEN;
header_token.score = score;
for (int i=0;i<Parser::peptide::SIZE;++i)
    new (&header_token.holds[i].link) shared_ptr<list<list_arg> >;
#warning "add to_find init"
};

失败的代码:

void save_stack(const std::list<stack_token*>& search, std::list<std::vector<stack_token> >& res)
{
    vector<AnalyseurPeptide::stack_token> l;
    auto i=search.begin();
    auto end = search.end();

    stack_token tmp = stack_token(0.f); /* if I remove this */
    l.emplace_back(tmp); /* and this, all is ok */

    while(i!=end)
   {
     l.emplace_back(**i); //< fail here
      ++i;
   }
   res.emplace_back(l);
}


推荐答案

如果使用C ++ 03,该代码是非法的,因为
C ++ 03不允许在
a联合中使用非平凡的默认构造函数,
复制构造函数,赋值运算符或析构函数的类型。在C ++ 11中,代码是非法的,因为如果联合
包含上述任何内容,则编译器将删除联合的
对应成员。因此,您的联合没有默认的
构造函数,复制构造函数,赋值或析构函数。
表示您无法实例化它,或以任何方式使用它。其中
表示 A :: A(int)所需的默认构造函数不存在
,并且当您使用该编译器时会抱怨定义此
函数(或 A 的任何构造函数)。

If you're compiling with C++03, the code is illegal, because C++03 doesn't allow types with non-trivial default constructors, copy constructors, assignment operators or destructors in a union. With C++11, the code is illegal, because if the union contains any of the above, the compiler deletes the corresponding member of the union. So your union has no default constructor, copy constructor, assignment or destructor. Which means you can't instantiate it, or use it in any way. And which means that the default constructor needed by A::A(int) doesn't exist, and that the compile should complain when you define this function (or any constructor of A).

如果编译器编译了此类代码,这意味着编译器
无法正确实现新的并集内容,因此
您将无法使用它。

If the compiler compiles such code, it means that the compiler doesn't implement the new union stuff correctly, and thus, that you cannot use it.

实际发生的情况:我怀疑
编译器在 A
的副本构造函数中使用按位复制(而不是拒绝生成它)。 vec.emplace_back(tmp)
使用复制构造函数在 vec 中创建新元素。
按位复制意味着您最终得到
a shared_ptr 的两个实例,它们指向同一个对象,但是两个
都有一个计数之1。第一个正确破坏,并且
第二个访问已删除的内存。繁荣。

With regards to what actually happens: I suspect that the compiler is using bitwise copy in the copy constructor of A (rather than refusing to generate it). vec.emplace_back(tmp) uses the copy constructor to create the new element in vec. Bitwise copy means that you end up with two instances of a shared_ptr which point to the same object, but which both have a count of 1. The first one destructs correctly, and the second accesses deleted memory. Boom.

解决问题的最简单方法是使用
boost :: variant (其中表示在工会之外的某个地方
中定义 struct 并为其命名)。如果由于某些原因您不能使用Boost,那么按照您正在做的事情,对于手动实现$ b而言,这相对来说是微不足道的。在
工会本身中,对于每个非POD成员,您只有 unsigned char token [
sizeof(TokenType)];
等,一些必要的
成员,以确保对齐(在大多数
处理器上, double 可以解决问题)。然后,在数组名称上使用
reinterpret_cast 获取指向所需类型的
指针,将其放置在新的位置以初始化它,以及显式的
销毁销毁销毁销毁销钉的销毁销货件的销毁销毁销毁销毁销毁销件的销毁销毁销毁叉车的销毁销毁销毁卷的销毁记录的金额,很大程度上与您所做的工作类似。
然后,您将实现一个有效的副本构造函数和一个赋值
运算符,并同时考虑类型。

The simplest way to solve your problem is to use boost::variant (which means defining the struct in the union somewhere outside of the union, and giving them a name). If for some reason you cannot use Boost, it's relatively trivial to implement by hand, along the lines of what you are doing. In the union itself, you just have unsigned char token[ sizeof(TokenType) ]; etc., for each non-POD member, with some additional members if necessary to ensure alignment (on most processors, a double will do the trick). You then use reinterpret_cast on the name of the array to get a pointer to the desired type, placement new to initialize it, and explicit destruction to destruct it, much along the lines you've done. And you implement a copy constructor and an assignment operator that work, and take into account the types as well.

(这并不困难。我已经完成了一两次:对于解析器中的
令牌,对我们从Excel中获得的表进行建模,
等)。

(It's not that difficult. I've done it one or two times: for tokens in a parser, for modeling tables which we get from Excel, etc.)

这篇关于shared_ptr上的新放置在删除时使seg错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆