c++ - 我的一些关于 List C++ 的函数有问题

标签 c++ list inheritance copying

我使用 ICircularDoubleDirectedList 作为类结构,然后我的列表继承自这个和那个文件,你可以在下面找到。我使用一个文件来测试列表中的深度复制,但问题是它通过了所有测试,除了最后一个应该删除所有数据的测试。

我已经测试了我所有的功能,它们都通过了测试,所以我不知道在哪里可以找到解决方案:/

我知道这可能需要看很多东西,如果有人能帮助我,我真的无法形容我会有多高兴! :D

这是我的列表的设计结构:

#ifndef ICDDLIST_H
#define ICDDLIST_H


template <typename T>
class ICircularDoubleDirectedList
{
public:
    static enum direction{FORWARD, BACKWARD};
    virtual ~ICircularDoubleDirectedList() {};                      //gjord
    virtual void addAtCurrent(const T& element) = 0;                //gjord
    virtual T& getElementAtCurrent() const = 0;                     //gjord
    virtual bool removeAtCurrent(const T& element) = 0;                             
    virtual int size() const = 0;                                   //gjord
    virtual void changeDirection() = 0;                             //gjord     
    virtual void moveCurrent() = 0;                                 //gjord
    virtual typename direction getCurrentDirection() const = 0;     //gjord
};

#endif

___________________________________________________________________________

这是我的定义和声明。是的,我知道它们应该在两个不同的头文件和 .cpp 文件中,但我认为测试它们是否在同一个文件中更容易。

#ifndef DOUBLELIST_H
#define DOUBLELIST_H

#include "ICircularDoubleDirectedList.h"

using namespace std;

template <typename T>
class CircularDoubleDirectedList : public ICircularDoubleDirectedList <T>{

private:

    class Node{
    public:

        T data;
        Node *neighbors[2];

        Node(const T& element);
        Node(){};
        ~Node(){};


    };

    direction NodeDirection = FORWARD;
    Node *current;
    int sizeOfList;

public:

    CircularDoubleDirectedList();
    CircularDoubleDirectedList(const CircularDoubleDirectedList &other);
    ~CircularDoubleDirectedList();

    virtual void addAtCurrent(const T& element);

    T& getElementAtCurrent() const;
    bool removeAtCurrent(const T& element);
    int size() const;
    void changeDirection();
    void moveCurrent();
    typename direction getCurrentDirection() const;


    bool operator=(const CircularDoubleDirectedList &other);


};




template <typename T>
CircularDoubleDirectedList<T>::Node::Node(const T& element){

    data = element;

}

template <typename T>
CircularDoubleDirectedList<T>::~CircularDoubleDirectedList(){

    if (this->size() != 0){
        while (0 < this->sizeOfList){
            this->removeAtCurrent(this->getElementAtCurrent());
        }
    }
    //this->current = nullptr;
}

template <typename T>
CircularDoubleDirectedList<T>::CircularDoubleDirectedList(){

    NodeDirection = FORWARD;
    sizeOfList = 0;
    current = nullptr;

}

template <typename T>
CircularDoubleDirectedList<T>::CircularDoubleDirectedList(const CircularDoubleDirectedList &other){

    this->NodeDirection = other.NodeDirection;
    this->current = other.current;
    this->sizeOfList = 0;

    if (other.sizeOfList != 0){
        Node *counter = other.current;

        for (int x = 0; x < other.sizeOfList; x++){
            counter = counter->neighbors[BACKWARD];
            this->addAtCurrent(counter->data);
        }
    }
    else{
        this->current = nullptr;
    }

}




template <typename T>
void CircularDoubleDirectedList<T>::addAtCurrent(const T& element){

    Node *temp = new Node(element);

    if (current == nullptr){
        current = temp;
        temp->neighbors[FORWARD] = temp;
        temp->neighbors[BACKWARD] = temp;


    }

    else{
        temp->neighbors[FORWARD] = current;
        temp->neighbors[BACKWARD] = current->neighbors[BACKWARD];
        temp->neighbors[BACKWARD]->neighbors[FORWARD] = temp;

        current->neighbors[BACKWARD] = temp;

        current = temp;
    }

    ++sizeOfList;

}

template <typename T>
T& CircularDoubleDirectedList<T>::getElementAtCurrent() const{

    if (sizeOfList <= 0){
        throw "Exeption: call of getElementAtCurrent on empty list";
    }

    else{
        return current->data;
    }

}



template <typename T>                   //INTE FEL PÅ
bool CircularDoubleDirectedList<T>::removeAtCurrent(const T& element){                      
    bool success = false;

    Node *temp = this->current;

    int x = 0;
    if(sizeOfList <= 0){
        throw "Exeption: call of removeAtCurrent on empty list";
    }

    else if (sizeOfList==1 && current->data==element){
        delete current;
        this->current = nullptr;
        this->sizeOfList--;
        success = true;
    }

    while(x<this->sizeOfList && success==false ) {
        if (temp->data == element){
            if (temp == this->current){
                this->moveCurrent();
            }

            temp->neighbors[BACKWARD]->neighbors[FORWARD] = temp->neighbors[FORWARD];
            temp->neighbors[FORWARD]->neighbors[BACKWARD] = temp->neighbors[BACKWARD];

            delete temp;
            this->sizeOfList--;
            success = true;


        }
        else{
            temp = temp->neighbors[FORWARD];
        }
        x++;
    }

    return success;
}

template <typename T>
int CircularDoubleDirectedList<T>::size() const{

    return sizeOfList;

}

template <typename T>
void CircularDoubleDirectedList<T>::changeDirection(){


    if (NodeDirection == FORWARD){
        NodeDirection = BACKWARD;
    }
    else
        NodeDirection = FORWARD;

}


template <typename T>
void CircularDoubleDirectedList<T>::moveCurrent(){

    if (NodeDirection == FORWARD && sizeOfList>0){
        current = current->neighbors[FORWARD];
    }
    else if (NodeDirection == BACKWARD && sizeOfList>0){
        current = current->neighbors[BACKWARD];
    }
    else{
        throw "Exception: call of moveCurrent on empty list";
    }
}

template <typename T>
typename ICircularDoubleDirectedList<T>::direction CircularDoubleDirectedList<T>::getCurrentDirection() const{

    return NodeDirection;

}



template <typename T>   //inte fel på
bool CircularDoubleDirectedList<T>::operator=(const CircularDoubleDirectedList &other){


    if (&other == this){
        return true;
    }

    if (this->size() != 0){
        Node *temp1 = this->current;
        T temp2;

        while (0 < this->sizeOfList){
            temp2 = temp1->data;
            temp1 = temp1->neighbors[FORWARD];
            this->removeAtCurrent(temp2);
        }
        this->current = nullptr;
    }

    this->NodeDirection = other.NodeDirection;

    if (other.size() > 0){

        Node *counter = other.current;

        for (int x = 0; x < other.size(); x++){
            counter = counter->neighbors[BACKWARD];
            this->addAtCurrent(counter->data);
        }
    }
    else{
        this->current = nullptr;
    }

    return true;

}




#endif

这是我用来测试我的列表的文件:

#include "CircularDoubleDirectedList.h"
#include <iostream>

using namespace std;
template <typename T>
void printList(CircularDoubleDirectedList<T>& list)
{
    for (int i=0; i<list.size(); i++)
    {
        cout<<list.getElementAtCurrent()<<" ";
        list.moveCurrent();
    }
}

template <typename T>
void test(CircularDoubleDirectedList<T> list)
{
    list.addAtCurrent(55);
}

int main()
{
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    CircularDoubleDirectedList<int> aList;
    CircularDoubleDirectedList<int> bList = aList;

    cout<<"******** Testing copy constructor on empty list ********"<<endl;
    cout<<endl<<"Expected output: \nElements in aList: \nElements in bList"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in aList ";
    printList(aList);

    cout<<endl<<"Elements in bList ";
    printList(bList);
    cout<<endl;
    system("pause");
    cout<<endl<<"******** Testing copy constructor on list with content ********"<<endl;

    aList.addAtCurrent(10);
    aList.addAtCurrent(20);

    CircularDoubleDirectedList<int> cList = aList;

    cout<<endl<<"Expected output: \nElements in aList 20 10\nElements in cList 20 10"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in aList ";
    printList(aList);

    cout<<endl<<"Elements in cList ";
    printList(cList);

    aList.removeAtCurrent(20);
    cList.addAtCurrent(5);

    cout<<endl<<endl<<"Expected output: \nElements in cList 5 20 10"<<endl<<endl<<"Your output: "<<endl;

    test(cList);

    cout<<"Elements in cList ";
    printList(cList);

    cout<<endl;
    system("pause");

    CircularDoubleDirectedList<int> dList;
    CircularDoubleDirectedList<int> eList;


    cout<<endl<<endl<<"******** Testing assignment operator on empty list ********"<<endl;
    dList = eList;
    cout<<endl<<"Expected output: \nElements in dList \nElements in eList"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in dList ";
    printList(dList);

    cout<<endl<<"Elements in eList ";
    printList(eList);
    cout<<endl;
    system("pause");

    cout<<endl<<endl<<"**** Testing assignment operator on list with content assigned empty list****"<<endl;
    eList.addAtCurrent(20);
    eList.addAtCurrent(10);

    eList = dList;

    cout<<endl<<"Expected output: \nElements in dList\nElements in eList"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in dList ";
    printList(dList);

    cout<<endl<<"Elements in eList ";
    printList(eList);
    cout<<endl;
    system("pause");
    cout<<endl;

    cout<<endl<<"***** Testing assignment operator on empty list assigned list with content *****"<<endl;
    eList.addAtCurrent(20);
    eList.addAtCurrent(10);

    dList = eList;

    cout<<"Expected output: \nElements in dList 10 20 \nElements in eList 10 20"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in dList ";
    printList(dList);

    cout<<endl<<"Elements in eList ";
    printList(eList);
    cout<<endl;

    system("pause");

    dList.addAtCurrent(5);
    eList.removeAtCurrent(20);
    cout<<endl<<"Expected output: \nElements in dList 5 10 20 \nElements in eList 10"<<endl<<endl<<"Your output: "<<endl<<endl;
    cout<<"Elements in dList ";
    printList(dList);

    cout<<endl<<"Elements in eList ";
    printList(eList);
    cout<<endl;
    system("pause");

    cout<<endl<<"***** Testing assignment operator on lists with content *****"<<endl;

    eList = dList;

    cout<<"Expected output: \nElements in dList 5 10 20 \nElements in eList 5 10 20"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in dList ";
    printList(dList);

    cout<<endl<<"Elements in eList ";
    printList(eList);
    cout<<endl;

    system("pause");

    eList.addAtCurrent(1);
    dList.removeAtCurrent(10);

    cout<<endl;
    cout<<"Expected output: \nElements in dList 5 20 \nElements in eList 1 5 10 20"<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in dList ";
    printList(dList);

    cout<<endl<<"Elements in eList ";
    printList(eList);
    cout<<endl;

    system("pause");

    cout<<endl<<"***** Testing assignment operator on a list assigned itself *****"<<endl;

    dList = dList;

    cout<<endl<<"Expected output: \nElements in dList 5 20 "<<endl<<endl<<"Your output: "<<endl;
    cout<<"Elements in dList ";
    printList(dList);
    cout<<endl;

    system("pause");


    cout<<endl<<"***** Testing destructor of list *****"<<endl;


    ICircularDoubleDirectedList<int>* fList = new CircularDoubleDirectedList<int>();


    fList->addAtCurrent(100);           
    fList->addAtCurrent(50);


    delete fList;               //NÅgot fel här 

    return 0;
}

最佳答案

你的代码有几个问题

第一个是基于样式的。无需在引用当前对象的所有行中指定 this->。这样做会使代码困惑,没有必要。

其次,您编写的复制/赋值运算符错误且风格不佳。但是,我会赞扬您使用 addAtCurrent 函数编写复制构造函数。通常我看到有人用各种指针逻辑编写复制构造函数,复制他们已经在他们编写的 add 成员函数中编写的代码。你没有犯那个错误,所以我赞扬你。

CircularDoubledirectList 的复制构造函数不需要执行任何 if 语句。每当我在复制构造函数中看到 if 语句时,都会发出危险信号。如果复制构造函数中有太多基于决策的逻辑,那么您最终得到的很可能不是真正的拷贝,而是半生不熟的拷贝。漂浮在程序中的半生不熟的拷贝是很难发现的错误,因为它会导致发生未定义的行为。

所以这里是不使用 if 的复制构造函数的重写:

template <typename T>
CircularDoubleDirectedList<T>::CircularDoubleDirectedList(const CircularDoubleDirectedList &other) : 
                        NodeDirection(other.NodeDirection), 
                        sizeOfList(0), current(nullptr)
{
    Node *counter = other.current;
    for (int x = 0; x < other.sizeOfList; ++x)
    {
        counter = counter->neighbors[BACKWARD];
        addAtCurrent(counter->data);
    }
}

上面的代码使用成员初始化列表 将某些成员初始化为它们的“空”值。请注意,循环不检查大小——没有必要。如果 other 的大小为 0,则循环不执行。

鉴于以上内容,让我们根据复制构造函数重写赋值运算符。当前赋值运算符实现的一个大问题是您要返回 bool。您应该返回的是对 CircularDoubleDirectedList 的引用。

赋值运算符的第二个问题是它是多余的。您编写的所有代码都已在复制构造函数中。我要修复它的方法是使用 copy/swap idiom,您将在其中使用复制构造函数来“帮助”完成赋值。

下面是使用这个习语的实现:

#include <algorithm>
//...
template <typename T>
class CircularDoubleDirectedList : public ICircularDoubleDirectedList <T>{
private:
    void swap(CircularDoubleDirectedList<T>& left, 
              CircularDoubleDirectedList<T>& right);
//...
};
//...
template <typename T>
void CircularDoubleDirectedList<T>::swap(CircularDoubleDirectedList<T>& left, CircularDoubleDirectedList<T>& right)
{
    std::swap(left.NodeDirection, right.NodeDirection);
    std::swap(left.current, right.current);
    std::swap(left.sizeOfList, right.sizeOfList);
}

template <typename T>  
CircularDoubleDirectedList<T>& CircularDoubleDirectedList<T>::operator=(const CircularDoubleDirectedList &other)
{
    CircularDoubleDirectedList<T> temp(other);
    swap(*this, temp);
    return *this;
}

那么这里发生了什么?好吧,我所做的就是使用复制构造函数来创建一个临时对象。然后我调用 swap 函数在临时拷贝和 *this 之间切换变量。然后我返回当前对象 (this)。这不仅测试了复制构造函数,还测试了 temp 的析构函数。因此,如果复制构造函数或析构函数中存在任何错误,您可以在这里检测到它们。

我添加了一个 swap 函数来调用 std::swap 来切换每个成员。

由于您的代码测试了赋值运算符,而您没有使用正确的返回类型正确实现它,请将您的代码更改为上面的代码并再次测试。

通过上述更改,我没有遇到任何内存损坏问题,程序成功完成。并不是说某处可能没有错误(我没有完成添加和删除项目的逻辑),但在对您的程序进行更改后,我运行代码时没有遇到任何问题。

关于c++ - 我的一些关于 List C++ 的函数有问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25952149/

相关文章:

c++ - 为什么我的彩色立方体不适用于 GL_BLEND?

c++ - 将任何数据类型打包到 vector <uint8_t>

c++ - 写入文件时保留空格

Java继承结构问题

c++ - wxWidgets 中的自定义内存分配

c++ - 给定一个节点数为奇数的单链表,通过只遍历链表一次找到中间节点的两种方法是什么?

python - 如何将列表保存到文件并将其作为列表类型读取?

python - 查找和替换列表中的字符串值

c++ - 在派生类中更改功能访问模式

javascript - 子类互相覆盖对方的方法