C++11 动态分配的双链接循环列表类实例segc++;

C++11 动态分配的双链接循环列表类实例segc++;,c++11,linked-list,segmentation-fault,dynamic-programming,C++11,Linked List,Segmentation Fault,Dynamic Programming,当main使用dlring类型的构造变量进行操作时,使用此模板类的效果非常好,但我的目标是允许动态分配,因此我可以处理非预定义数量的双链接循环列表,以允许使用以下函数: 使用节点位置(通过 迭代)或值输入 将两个列表链接成一个具有单个头/尾的列表也是如此 配对 节点从一个列表(实例)导出到另一个列表(实例) 等等 我非常确定有一个优雅的解决方法,我还不知道,但我认为如果你没有努力解决问题,为社区提出一个问题是不好的。(点击谷歌) 因此,有了这些目标,我应该使用某种指向指针的指针(AFAIK)

当main使用dlring类型的构造变量进行操作时,使用此模板类的效果非常好,但我的目标是允许动态分配,因此我可以处理非预定义数量的双链接循环列表,以允许使用以下函数:

  • 使用节点位置(通过
    迭代)或值输入
  • 将两个列表链接成一个具有单个头/尾的列表也是如此 配对
  • 节点从一个列表(实例)导出到另一个列表(实例)
  • 等等
我非常确定有一个优雅的解决方法,我还不知道,但我认为如果你没有努力解决问题,为社区提出一个问题是不好的。(点击谷歌)

因此,有了这些目标,我应该使用某种指向指针的指针(AFAIK)来动态分配内存(通过构造函数调用)。如果有更聪明的方法来实现这些,请让我知道。我的解决方案尝试在这段代码的最后给出。请随意批评下面的所有内容

双链接循环列表类标题(简化)
模板
类数据环
{
结构节点
{
T数据;
节点*prev;
节点*下一步;
node(T,node*p,node*n):数据(T),上一个(p),下一个(n){
};
节点*头;
节点*尾部;
公众:
dlring():head(nullptr),tail(nullptr){}
bool empty()常量{return(!head | | | tail)}
//运算符bool()常量{return!empty();}
空隙推力(T);
T弹回();
~dlring()
{
while(head)
{
节点*温度(头部);
头部=头部->下一步;
删除临时文件;
}
}
};
我应该使用注释掉的运算符bool重载吗

弹回和推送方法:
模板
void dlring::推送(T数据)
{
头部=新节点(数据、尾部、头部);
如果(头->下一步)
{
头部->下一步->上一步=头部;
尾部->下一个=头部;
}
if(空())
{
尾=头;
头部->下一个=尾部;
头部->前方=尾部;
尾部->下一个=头部;
尾部->上一个=头部;
}
}
模板
T dlring::pop_back()
{
if(空())
库特普雷夫;
如果(尾部!=温度)
{
尾部->下一步->下一步=头部;
头部->前方=尾部;
}
其他的
{
水头=零PTR;
tail=nullptr;
}
删除临时文件;
温度=零PTR;
返回数据;
}
我的尝试没有正确的行为:当我试图通过迭代显示所有列表时,代码失败,在dlist[0]的head->data access trument中出现segfaulting,其中0是k的迭代。下面是代码片段:

   int main()
    {
    int k;
      std::cout<<"Rings count?"<<std::endl;
      std::cin>>k;
        dlring<int>* dlist = new dlring<int>[k]; //I suppose I'm allocating *k*
     //dlring<int> elements. this line is not confirmed to call the constructor.
    (dlist[0]).Push(10);
    (dlist[0]).Push(13);
    (dlist[1]).Push(99);
    /*{
    while(!dlist[0].empty())
    std::cout<<(dlist[0]).pop_back()<<" ";
    std::cout<<std::endl;
    while(!dlist[1].empty())
    std::cout<<(dlist[1]).pop_back()<<" ";
    }*/
    //this section works perfectly fine, while this
      for(int i=0;i<k;i++)
      {
        while(!dlist[k].empty())
        std::cout<<(dlist[k]).pop_back()<<" ";
        std::cout<<std::endl;
      }
    //is causing a segmentation fault while attempting to access dlist[*0*].tail->data.
    std::cout<<(dlist[0]).head->data;
    //line was checked and is confirmed to be functional, 
    //I suppose dlist[variable] has some trick I don't know yet.
    //what I wish to look like an instance call would be *
    return 0;
    }
intmain()
{
int k;
标准::cout
由于
dlist
是一个大小为
k
的数组,因此原始代码生成一个越界访问


由于前面提到的循环使
dlist
数组中的每个列表都为空,因此所有列表的
头将是一个空指针。请注意,由于它是一个私有成员,因此您根本无法访问它。如果您这样做,则在取消引用它时将得到一个segfault:

std::cout<<(dlist[0]).head->data;
通过这些更改,我不会从clang的地址消毒剂中得到任何错误、问题或报告


另一个问题(在您的
main
中没有体现)是
pop\u back
中的
tail
设置。我将尝试使用一些ASCII艺术进行说明。一个框

   D ->
<- D
head->prev
指向与
tail
相同的对象,类似地,
tail->next
指向与
head
相同的对象

现在,一个“动画”的
pop_back
功能

template<typename T>
T dlring<T>::pop_back()
{
    if( empty() )
        std::cout<<"List empty";
    node* temp(tail);

    /*
       +-----------------------------+
       v                             |
       D ->    D -> ..    D ->    D -+
    +- D    <- D    .. <- D    <- D
    |                             ^
    +-----------------------------+
       ^                          ^
       |head                      |tail
                                  |temp
    */

    T data( tail->data );
    tail = tail->prev ;

    /*
       +-----------------------------+
       v                             |
       D ->    D -> ..    D ->    D -+
    +- D    <- D    .. <- D    <- D
    |                             ^
    +-----------------------------+
       ^                  ^       ^
       |head              |tail   |temp
    */

    if (tail != temp)
    {
        tail->next->next = head; 

        /*
           +-----------------------------+
           v                             |
           D ->    D -> ..    D ->    D -+  (A)
        +- D    <- D    .. <- D    <- D
        |                             ^
        +-----------------------------+
           ^                  ^       ^
           |head              |tail   |temp

        The pointer (A) is what is changed when writing to tail->next->next.
        Yes, nothing has actually changed in the list!
        */

        head->prev = tail;

        /*
           +-----------------------------+
           v                             |
           D ->    D -> ..    D ->    D -+
        +- D    <- D    .. <- D    <- D
        |                     ^
        +---------------------+
           ^                  ^       ^
           |head              |tail   |temp
        */

    }
    else
    {
        head = nullptr;
        tail = nullptr;
    }
    delete temp;

    /*
       D ->    D -> ..    D ->
    +- D    <- D    .. <- D
    |                     ^
    +---------------------+
       ^                  ^       ^
       |head              |tail   |temp
    */

    temp = nullptr;
    return data;
}
删除
temp
(无进一步更改)后,它将如下所示:

        /*
           +---------------------+
           v                     |
           D ->    D -> ..    D -+
        +- D    <- D    .. <- D
        |                     ^
        +---------------------+
           ^                  ^       ^
           |head              |tail   |temp
/*
+---------------------+
五|
D->D->…D-+

+-D在google://Overloading operators//Selector T*operator->{return m_obj;}//Address access T&operator*(){return*m_obj;}中漫游时发现此问题这有什么帮助吗?我认为
pop\u back
中尾部的设置是不正确的:
tail->next->next=head;
在这一点上,
tail
已经指向了新的尾部,所以我会设置
tail->next=head;
@dyp是的,但是要做新的tail->next=head我通过新的tail->next引用旧的tail。希望我没有弄错。也许我误解了
tail
的目的,但我就是不明白:
head->prev==tail&&tail->next==head
是否保证所有非空列表(当然是在成员函数之外)?如果是这样,为什么需要两个数据成员?如果牺牲一点空间并使用一个头节点(如果il不是集合的一部分),那么您的代码将更加简单和快速,因为可以从header节点访问极端值(head和tail)。并且您不必担心边界条件(push()为空列表,pop_back()为带一个元素的列表)。此外,如果在列表变为空之前使用pop_back(),则析构函数也更简单、更安全。请看一看我的main,我在尝试通过(dlist[k=0])调用pop_back访问数据(tail->data)时遇到segfault错误,但是(dlist[0])运行正常。请记住tail->data指的是“新”tail(从弹出结束或首次尝试弹出开始,两者都是合法的).P.S.:最后,我设法以一种可读的方式写下了这篇评论,只要我能对你的努力进行评价就好了。非常感谢你的关注。@Гааааааааааааааааааааааа这两个额外的变化,我很有信心,程序应该运行良好。请看我的答案的开头。好的,我们这里有一个很好的实现
std::cout<<(dlist[0]).head->data;
delete[] dlist;
   D ->
<- D
   +-----------------------------+
   v                             |
   D ->    D -> ..    D ->    D -+
+- D    <- D    .. <- D    <- D
|                             ^
+-----------------------------+
   ^                          ^
   |head                      |tail
template<typename T>
T dlring<T>::pop_back()
{
    if( empty() )
        std::cout<<"List empty";
    node* temp(tail);

    /*
       +-----------------------------+
       v                             |
       D ->    D -> ..    D ->    D -+
    +- D    <- D    .. <- D    <- D
    |                             ^
    +-----------------------------+
       ^                          ^
       |head                      |tail
                                  |temp
    */

    T data( tail->data );
    tail = tail->prev ;

    /*
       +-----------------------------+
       v                             |
       D ->    D -> ..    D ->    D -+
    +- D    <- D    .. <- D    <- D
    |                             ^
    +-----------------------------+
       ^                  ^       ^
       |head              |tail   |temp
    */

    if (tail != temp)
    {
        tail->next->next = head; 

        /*
           +-----------------------------+
           v                             |
           D ->    D -> ..    D ->    D -+  (A)
        +- D    <- D    .. <- D    <- D
        |                             ^
        +-----------------------------+
           ^                  ^       ^
           |head              |tail   |temp

        The pointer (A) is what is changed when writing to tail->next->next.
        Yes, nothing has actually changed in the list!
        */

        head->prev = tail;

        /*
           +-----------------------------+
           v                             |
           D ->    D -> ..    D ->    D -+
        +- D    <- D    .. <- D    <- D
        |                     ^
        +---------------------+
           ^                  ^       ^
           |head              |tail   |temp
        */

    }
    else
    {
        head = nullptr;
        tail = nullptr;
    }
    delete temp;

    /*
       D ->    D -> ..    D ->
    +- D    <- D    .. <- D
    |                     ^
    +---------------------+
       ^                  ^       ^
       |head              |tail   |temp
    */

    temp = nullptr;
    return data;
}
    if (tail != temp)
    {
        tail->next = head; 

        /*
           +---------------------+-------+
           v                     |       |
           D ->    D -> ..    D -+    D -+
        +- D    <- D    .. <- D    <- D
        |                             ^
        +-----------------------------+
           ^                  ^       ^
           |head              |tail   |temp
        /*
           +---------------------+
           v                     |
           D ->    D -> ..    D -+
        +- D    <- D    .. <- D
        |                     ^
        +---------------------+
           ^                  ^       ^
           |head              |tail   |temp