c++ 如何识别对象是否在堆栈或堆内存中

z3yyvxxp  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(152)

我最近收到了我的数据结构课程的大学作业,要求我用C++创建一个双向链表
在编写双向链表时,我需要实现各种功能,但有一个方法特别引起了我的注意,它的名字叫**“clear()"。这个方法负责清除双向链表中的所有元素**:

void clear(Node* head_ptr)
{
    Node* previous_ptr = nullptr;

    while(head_ptr != nullptr)
    {
        previous_ptr = head_ptr; // Store previous node.

        head_ptr = head_ptr->next;

        delete previous_ptr;
    }
};

字符串
该方法非常简单;它只是迭代所有元素并为每个Node释放内存。然后我在析构函数中**调用此方法,如下所示:

~List()
{
    clear_list(m_head_ptr);
};


然后我开始思考,这种释放内存的方法很好如果我的节点元素在堆上,就像这样:

int main()
{
    List list;

    Node* node_1 = new Node(3, nullptr);    // The tail node.
    Node* node_2 = new Node(1, node_1);
    Node* node_3 = new Node(5, node_2);
    Node* node_4 = new Node(7, node_3);     // The head node. 
    
    list.add(node_1);
    list.add(node_2);
    list.add(node_3);
    list.add(node_4);

    // Then do some stuff with the list...

}   // The list goes out of scope and the destructor is called...


但是,当我在堆栈上创建**Node s**并传递一个指向堆栈对象的指针时,这个过程就被打破了,就像这样:

int main()
{
    List list;

    Node* node_1(3, nullptr);    // The tail node.
    Node* node_2(1, node_1);
    Node* node_3(5, node_2);
    Node* node_4(7, node_3);     // The head node. 
    
    list.add(&node_1);
    list.add(&node_2);
    list.add(&node_3);
    list.add(&node_4);

    // Then do some stuff with the list...

}   // The list goes out of scope and the destructor is called and the program crashes because it attempts to free stack objects...


原因是因为我试图释放堆栈对象,这不是一个好主意。当然,我们通常不会使用基于堆栈的Node s,因为我们通常希望Node数据持久化到它们创建的范围之外。然而,这导致了我的问题:
我该怎么反击?有没有办法检查内存中的一些Node是否在我的函数中的堆或堆栈上,然后相应地释放它?或者,有没有更好的方法来解决这个问题?

xkrw2x1b

xkrw2x1b1#

最简单和最有效的解决方案是 * 通常 * 让列表自己管理节点的分配。用户甚至不需要知道node类型的存在,更不用说分配它们等等。
所以,对于用户来说,你在问题中展示的内容的等价物是这样的:

List list;
list.add_head(1);
list.add_head(3);
list.add_head(5);
list.add_head(7);

字符串
你通常还需要一个add_tail来将元素添加到列表的末尾。每个add(头或尾)通常应该返回一个抽象的iterator类型(它可能是指向节点的指针的 Package 器),所以你可以这样做:

auto pos = list.add_head(7);
list.add_tail(5);
list.add_after(pos, 3);


.这将增加7在开始,5在结束,和3后的7
这样,列表本身就分配了所有节点,并知道如何处置它们。您可以更进一步,让它将分配和处置委托给Allocator类。这当然很有用,但可能有点超出了基本练习的意义(在实际使用中,您可能希望使用标准库中的容器--尽管标准库确实提供了单链表和双链表,#21518;很少有用)。

ngynwnxp

ngynwnxp2#

因为你刚刚开始学习动态内存,所以你必须从头开始学习(必须有人编写库-有一天可能是你!)。
所以,你会使用链表的一个原因是因为你不知道有多少节点会被提前添加。同样,在基于堆栈的节点数据的链表上放置“MAX_SIZE”的实现也不好(MAX_SIZE足够大吗?对于你的链表的一些客户端来说,它是否太大了?)
因此,链表几乎总是使用动态内存实现的。在C中,这将是malloc/free。在C++中,这将是new/delete。最终,客户端代码将利用标准库模板化实现(就像许多人指出的那样)。
为了回答你的问题,在你的教育中,使用动态内存,只为你的链表节点使用new/delete。
你很快就会明白为什么你(最终)会使用标准库实现,因为你必须了解使用/操作手工链表时的所有边缘情况(解引用删除的指针,内存泄漏等)。
很多时候,你通过失败学到更多(例如如何使用调试器!)。

相关问题