1

I have to overload '+' operator for two dynamic containers.

Occurance Occurance::operator +(const Occurance& occ) const {
    Occurance* result = new Occurance;
    Iterator i1(head);
    Iterator i2(occ.head);
    while( !(i1.isNULL() && i2.isNULL()) ) {
        if(i1.getCount() >= i2.getCount()) {
            result->add(i1.getFile());
            result->tail->count = i1.getCount();
            ++i1;
        }
        else {
            result->add(i2.getFile());
            result->tail->count = i2.getCount();
            ++i2;
        }
    }
    return *result;
}

When I do:

Occurance occ = occ1+occ2;

Pointers to the begin of the list are copied correctly and everything works fine but I'm losing reference to result. When occ destructor is called whole list is destroyed, but not the first element of result as I have simply copied it's content instead of reference.

When I change return type to reference the same occurs but during assignment.

Another idea is to not create the 'result' dynamically, so it's automaticly destroyed at the end of function, but then it's calling the destructor which is destroying whole list.

Is there any simple and "proper" way to create such structure and return it without this memory leak? And of course the returned type must be the object or reference as it is expected from '+' operator.

I have figured out a nasty hack involving changing pointer to function in the destructor, but maybe I'm just missing something very simple?

Edit: Of course class follows the rule of three. Here is assignment:

Occurance& Occurance::operator =(const Occurance& occ) {
    destruct();
    head = occ.head;
    current = occ.current;
    tail = occ.tail;
    return *this;
}

Occurance::Occurance(const Occurance& occ) {
    head = occ.head;
    current = occ.current;
    tail = occ.tail;
}

Occurance::~Occurance() {
    destruct();
}

destruct just destroys the list that starts at 'head'.

The class declaration:

class Occurance {
private:
    class Node {
    public:
        Node* next;
        Node* prev;
        int count;
        const File* file;

        Node(const File& a_file, Node* a_prev);
    };

    Node* head;
    Node* tail;
    Node* current;
    void destruct();
public:
    class Iterator {
    private:
        Node* node;
    public:
        Iterator();
        Iterator(Node* a_node);
        void operator ++();
        const File& getFile();
        int getCount();
        bool isNULL();
    };

    Occurance();
    Occurance(const Occurance& occ);
    void add(const File& a_file);
    Occurance& operator =(const Occurance& occ);
    Occurance operator +(const Occurance& occ) const;   //dodaje listy zachowując sortowanie
    Iterator begin() const;
    virtual ~Occurance();
};
4

3 回答 3

0

另一个想法是不要动态创建“结果”,因此它会在函数结束时自动销毁,但随后它会调用正在销毁整个列表的析构函数。

您应该阅读有关复制构造函数的内容。在调用返回对象的析构函数之前,调用复制构造函数将数据从该对象复制到将保存 occ1+occ2 操作结果的临时对象。我假设您有指向动态分配数据的成员,在这种情况下,当调用复制构造函数时,它会将指针分配给临时对象,而不是分配新内存并复制数据。您必须自己编写代码。我建议您阅读:http ://www.cplusplus.com/articles/y8hv0pDG/

另请注意,如果要执行此分配,则应以相同的方式重载 operator =

occ = occ1+occ2

编辑:对不起,我不能评论,你能复制你的班级声明吗?

于 2013-10-26T13:12:18.383 回答
0

在 C++ 中,一般原则是返回堆栈上的副本,而不是堆上分配的对象。因此,在这种情况下,您只需执行以下操作:

Occurance Occurance::operator +(const Occurance& occ) const 
{
    Occurance result;
    // do whatever
    return result; 
}

并称之为:

Occurance occ = occ1+occ2;

编译器足够聪明,可以理解不是复制而是重用您要返回的对象(这称为返回值优化或RVO)。

如果您出于某种原因确实需要该对象与您的函数中创建的完全相同的对象,那么您可以:返回一个智能指针(查找shared_ptr)或使用新的 C++11移动运算符

于 2013-10-26T13:27:28.827 回答
0

您的复制构造函数和赋值运算符已损坏。您要么需要对列表进行深层复制,要么需要实现某种共享语义(例如引用计数)。您似乎有一个链表,并且您只是在复制头和尾指针。因此,当您制作副本时,其中一个被销毁,它也会破坏另一个列表。

我假设您的默认构造函数和/或您的add函数会动态分配节点。然后您的复制构造函数和赋值运算符也需要动态分配节点,这些节点完全独立于被复制对象的节点。如果您可以使用 C++11,您还应该考虑实现移动构造函数和移动赋值运算符。

一旦这些功能都正确,您的 operator+ 应该如下所示:

Occurance Occurance::operator +(const Occurance& occ) const {
    Occurance result; // no dynamic allocation

    // operate on result

    return result;
}
于 2013-10-26T13:29:19.427 回答