析构函数删除函数返回动态结构中的副本

时间:2010-11-21 14:12:52

标签: c++ constructor destructor

大家好,没有问题。

所以我有一个实现单链表的模板类。程序中的一个函数返回其中一个列表。

psList<int> psObj::getList() const {
 return List;
}

所以正在发生的事情是在返回List的调用中,复制构造函数会在其中很好地完成其工作并创建列表的副本。然而,函数完成并超出范围并调用析构函数!突然,返回的链表被删除,因为这是我的析构函数所做的,删除列表并将其删除。

我理解我可以让返回类型指向复制列表的头部,并且一切都会很好,但问题是我仍然无法创建返回动态结构副本的函数,即使我想,我也想。

我被要求提供更多代码。

这是复制构造函数,它显然是一个深层复制

template<class psClass>
psList<psClass>::psList(const psList &original) {
    head = NULL;

    if(original.head != NULL) {
        psNode<psClass>* iterator = original.head;
        while(iterator != NULL) {
            pushback(iterator->data);
            iterator = iterator->next;
        }
    }
}

这是析构函数

template<class psClass>
psList<psClass>::~psList() {
    erase();
}

这是析构函数调用的擦除函数。

template<class psClass>
void psList<psClass>::erase() {
    psNode<psClass>* iterator = head;
    psNode<psClass>* buff;

    while(iterator != NULL) {
        buff = iterator->next;
        delete iterator;
        iterator = buff;
    }
}

所以是的,我正在做深刻的复制和深刻的破坏。问题不在于深度。因此问题是。在原始函数中,生成并返回深度副本。该函数超出范围,并在副本上调用深度析构函数。没有更多的副本。

为了更好地解释这里是调试器中的样子

getlist函数调用之前的原始列表。

head 0x616080
data 2
next 0x616060
data 12
next 0x0

这是getList函数内的“返回列表”列表

head 0x616080
data 2
next 0x616060
data 12
next 0x0

同样的事情。

以下是复制构造函数末尾的“original”和“this”列表。

“这”

head 0x63c900
data 2
next 0x63a940
data 12
next 0x0

“原始”

head 0x616080
data 2
next 0x616060
data 12
next 0x0

一切看起来都不错。

现在我们回到getList函数中,即将进入最后一个括号。

psList<int> psObj::getList() const {
 return List;
} // This bracket

此函数中的列表列表是您期望的

head 0x616080
data 2
next 0x616060
data 12
next 0x0

现在我们进入最后一个括号,在

的地方调用析构函数
/* 
 * No idea what the in chrg thing is or why the debugger is telling me it was
 * optimized out but I mentioned it here cause maybe it has something to do with my
 * problem
 */
this 0x7ffffffe650
__in_chrg value optimized out

// Look familiar? well it should cause it is the head of the list I returned.
head 0x63c900 
data 2
next 0x63a940
data 12
next 0x0

然后是bam!我刚刚复制并返回的列表被析构函数删除,因为它超出了范围。

在绕道之后重申我原来的问题。如何使用深层副本使函数返回动态结构,而不使析构函数销毁所述副本。

根据要求提供更多代码

// Simple single link node with default constructor initializing the link to NULL.
template <class psClass>
struct psNode {
    psClass data;
    psNode<psClass>* next;

    psNode() {
        next = NULL;
    }
};

和推回功能

template<class psClass>
void psList<psClass>::pushback(psClass object) {
    psNode<psClass>* ptr = new psNode<psClass>;
    ptr->data = object;

    if(head == NULL)
        head = ptr;
    else {
            //Have to find the tail now
        psNode<psClass>* tail;
        psNode<psClass>* iterator = head;
        while(iterator != NULL) {
            tail = iterator;
            iterator = iterator->next;
        }
        tail->next = ptr;
    }
}

是的,我知道跟踪尾巴会更容易。

这是psList类的定义:

template <class psClass>
class psList {
public:
    psList();
    ~psList();
    psList(const psList &original);
    psList(psNode<psClass>* _head);

    void erase();
    void pushfront(psClass object);
    void pushback(psClass object);
    bool isEmpty() const;
    psNode<psClass>* front() const;

private:
    psNode<psClass>* head;
};

尚未重载的赋值运算符。我计划在跳过这个障碍后加入它。

2 个答案:

答案 0 :(得分:2)

psList的拷贝构造函数似乎是浅拷贝而不是深拷贝。通常,如果您管理类中的资源,那么您需要非平凡的复制构造函数,赋值运算符和析构函数(“三巨头”)。请告诉我们psList的代码。

答案 1 :(得分:0)

您目前正在做的事情如下:

psList<int> psObj::getList() const { return psList<int>(List); }

这会创建成员List的副本,并将其复制到调用getList的框架。它的复制方式取决于你如何调用它。如果您从此数据构造新的psList对象,如

psList<int> newList = obj.getList(); // same as psList<int> newList(obj.getList());

使用了复制构造函数。通常RVO将两份副本缩减为一份副本。

或者,如果要复制到现有对象,例如

psList<int> newList;
newList = obj.getList();

通过赋值运算符将原始对象的状态替换为返回结果中的数据。如果您没有声明自己的一个,编译器将为您定义一个公共assingment运算符。但这只是对象的每个成员的副本。那就是:

psList & psList::operator=(const psList& src) {
  head = src.head;
}

所以在调用代码中会发生什么,如下所示:

psList<int> newList; // psList default constructor called
newList = obj.getList(); // 1) obj.List copied via copy constructor within getList
                         // 2) copy of obj.List copy-assigned to newList (simple copy of head pointer)
                         // 3) copy of obj.List destructed
// newList now has head pointing to destroyed data

在你的情况下不是你想要的,你应该做的是确保副本分配真正实现了预期的深层拷贝(参见copy-and-swap了解通过拷贝构造函数的方法)已经实施了。)

因此rule of three:如果您需要定义自己的析构函数,复制构造函数和复制赋值中的任何一个的实现,那么您需要将它们全部定义(或者至少声明复制赋值和复制ctor)私有以使您的类不可复制)。

顺便说一下,为什么不返回参考:

const psList<int> & psObj::getList() const { return List; }

并让调用函数决定是否复制?

psList<int> localList(localPsObj.getList());