如何在C++中返回列表

How to return a List in C++

本文关键字:返回 列表 C++      更新时间:2023-10-16

假设我有一个List类:

class List
{
    private:
        class Node{
            public:
                int data;
                Node* next;
            public:
                virtual ~Node()
                {
                    if (next != NULL)
                        delete next;
                }
        };
        Node* head;
    public:
        virtual ~List()
        {
            if (head != NULL)
            {
                delete head;
            }
        }
    public:
        void AddNode(int data);
        void DeleteNode(int data);
        //....  
};

现在我想实现一个函数,它以两个List引用作为参数,并返回一个新创建的List:

List SumTwoList(List& list_1, List& list_2)
{
    //here I create a new List list_3 and add some elements to it based on list_1 and list_2(add operation use dynamic allocation).
    //finally I want to return this list_3.
    return list_3;
}

我在这里感到困惑。我应该如何在SumTwoList()中创建此list_3。如果我只是让它成为一个局部变量,当函数返回时,list_3的析构函数将释放添加到列表中的所有节点,并销毁整个列表。但是,如果我进行动态分配来创建此list_3并返回指向它的指针,则用户有责任在使用后删除此列表。

我不知道如何从这两个想法中做出选择,我确信有一些更好的方法可以解决这个问题。谢谢你的建议。:-)

添加复制构造函数和赋值运算符(并修复析构函数):

class List
{
    private:
        typedef struct node{
            int data;
            struct node* next;
        }NODE;
        NODE* head;
    public:
    virtual ~List()  // Please fix this, as others have mentioned!
    { }
    List(const List& rhs) 
    {
       // this needs to be implemented
    }
    List& operator = (const List& rhs) 
    {
       // this needs to be implemented
    }
    //...
};

"这需要实现"是您需要填写的内容,以使副本发挥作用。

这能给你带来什么?首先,您现在可以按照您在问题中尝试的形式编写函数。也就是说,您可以安全地按值返回List,而无需进行任何进一步的编码来动态分配或删除列表。

List SumTwoList(List& list_1, List& list_2)
{
   List list_3;
   // do stuff to add data to list_3...
   //...
   //finally I want to return this list_3. 
   return list_3;
}

其次,假设我们要将一个列表复制到另一个列表。这可以使用"="或通过简单的复制构造轻松完成

如果你不想复制,那么通过将复制ctor和赋值设置为私有和未实现*来关闭复制,否则使用你的类的程序员可以选择复制,编译器也会在需要时进行复制。

换句话说,如果复制正确实现,那么像这样的简单程序应该可以工作:

int main()
{
   List lis1;
   // Add some nodes to lis1...
   //...
   // Assume that lis1 now has nodes...complete the copying test
   List lis2(lis1);
   List lis3;
   lis3 = lis1;
}

上面的程序应该没有内存泄漏,也没有崩溃,即使main()返回也是如此。如果没有,则复制和/或销毁被破坏。

*C++11允许您在定义函数时使用delete关键字,以比C++11之前的代码更容易的方式禁用复制构造函数和赋值运算符。

一个选项是返回指向新List 的指针

List * SumTwoList(List& list_1, List& list_2)
{
    List * pResultList = new List;
    // Iterate over all elements of list_1 and add (append) them to pResultList
    // Iterate over all elements of list_2 and add (append) them to pResultList
    return pResultList;
}

然而,它的缺点是调用者必须记住delete返回的对象。

相反,如果list_2的元素被附加到现有列表中(例如),那么API的设计可能会稍有不同

// Append contents of rhs to this. rhs stays as is
List::append( List const & rhs );

呼叫者可以将其称为

List list_1;
// work on list_1, such as add elements
List list_2;
// work on list_2, such as add elements
list_1.append( list_2 );

这接近std::list::splice(),但并不完全。splice()元素从rhs移动到this,但append()只是复制这些元素。

注意,您的析构函数有缺陷,它是delete'ing only the头,后面的元素被泄露了。

析构函数实现的示意图

List::~List() {
    while( head ) {
        struct node * oldHead = head;
        head = head->next;
        delete oldHead;
    }
    head = NULL;
}

您将返回一个列表的副本。如果List类的复制构造函数和赋值运算符将被实现,那么这将达到您所期望的效果。

class List
{
    private:
    //...
    public:
        List();
        List( const List& other);
        List& operator= ( const List& other);
    //...
};

然而,也许更好的选择是创建一个构造函数,它占用两个List,并通过合并它们来构造一个新的(像SumTwoList那样完成所有这些):

class List
{
    private:
    //...
    public:
        List( const List& first, const List& second) { // similar to SumTwoList
        }
    //...
};