Упражнение со связанным списком, что я делаю не так? - PullRequest
1 голос
/ 13 марта 2010

Привет всем. Я делаю упражнение со связанным списком, которое включает динамическое выделение памяти, указатели, классы и исключения. Будет ли кто-то готов критиковать это и сказать мне, что я сделал неправильно и что я должен был сделать лучше как в отношении стиля, так и в отношении предметов, которые я перечислил выше?

/*

Linked List exercise

*/

#include <iostream>
#include <exception>
#include <string>
using namespace std;

class node{
public:
    node * next;
    int * data;
    node(const int i){
        data = new int;
        *data = i;
    }
    node& operator=(node n){
        *data = *(n.data);
    }
    ~node(){
        delete data;
    }
};

class linkedList{

public:

    node * head;
    node * tail;
    int nodeCount;

    linkedList(){
        head = NULL;
        tail = NULL;
    }

    ~linkedList(){
        while (head){
            node* t = head->next;
            delete head;
            if (t) head = t;
        }
    }

    void add(node * n){
        if (!head) {
            head = n;
            head->next = NULL;
            tail = head;
            nodeCount = 0;
        }else {
            node * t = head;
            while (t->next) t = t->next;
            t->next = n;
            n->next = NULL;
            nodeCount++;
        }
    }

    node * operator[](const int &i){
        if ((i >= 0) && (i < nodeCount)) throw new exception("ERROR:  Invalid index on linked list.", -1);
        node *t = head;
        for (int x = i; x < nodeCount; x++) t = t->next;
        return t;
    }

    void print(){
        if (!head) return;
        node * t = head;
        string collection;
        cout << "[";
        int c = 0;
        if (!t->next) cout << *(t->data);
        else while (t->next){
            cout << *(t->data);
            c++;
            if (t->next) t = t->next;
            if (c < nodeCount) cout << ", ";
        }
        cout << "]" << endl;
    }

};

int main (const int & argc, const char * argv[]){

    try{
        linkedList * myList = new linkedList;
        for (int x = 0; x < 10; x++) myList->add(new node(x));
        myList->print();
    }catch(exception &ex){
        cout << ex.what() << endl;
        return -1;
    }   
    return 0;
}

Ответы [ 4 ]

7 голосов
/ 13 марта 2010

Нет необходимости, чтобы данные были указателем.

Используйте список ctor-initializer, он лучше для правильности const и безопасности исключений. Ни один из ваших конструкторов не должен иметь никакого кода внутри тела.

Ваш конструктор связанного списка не инициализировал nodeCount.

Вы не используете хвостовой указатель вашего списка ни для чего. Это сохранит ваше сканирование по всему списку в непустом случае добавления - если вы обновите его.

Индексирование (оператор []) - необычная вещь для поддержки в связанном списке. ОТО, вы не сделали функцию удаления.

operator [] не должен принимать свой параметр по ссылке. Только константные ссылки должны быть переданы по большой структуре, маленький тип, такой как int, должен просто быть передан по значению.

Прямо сейчас, если добавить не удастся, указатель на новый узел () утечки. (Но на самом деле я не вижу пути для сбоя добавления, если ссылки на список не были повреждены.)

Вы должны определить личный конструктор копирования на узле, чтобы предотвратить двойное освобождение данных. Каждый раз, когда вы определяете operator = и destructor, вы также должны определять или удалять конструктор копирования (правило трех). Вы также должны определить конструктор частной копии и оператор присваивания для списка ссылок, чтобы избежать двойного освобождения.

Переменная коллекция строк в вашей функции печати не используется. Переменная t в print () должна быть указателем на const. Сама print должна быть функцией-членом const.

4 голосов
/ 13 марта 2010
/*
Linked List exercise
*/

class node {
public:
    node * next;
    int * data;

data на самом деле не обязательно быть указателем (если только в вашем классе не указано, что так и должно быть). Вы можете изменить это значение на int, а затем изменить ссылки с *(t->data) на t->data. Точно так же вам не нужно будет использовать new и delete в ctor / dtor.

    node(const int i) {
        data = new int;
        *data = i;
    } 
    node& operator=(node n) {
        *data = *(n.data);
    } 
    ~node() {
        delete data;
    } 
};

class linkedList {

public:
    node * head;
    node * tail;
    int nodeCount;

    linkedList() {
        head = NULL;
        tail = NULL;

Вы должны инициализировать всех ваших членов данных здесь. nodeCount будет иметь какое-то случайное значение, если вы не установите его здесь на ноль.

    } 

    ~linkedList() {
        while (head) {
            node* t = head->next;
            delete head;
            if (t)
                head = t;

Это похоже на ошибку. Вам необходимо всегда назначать head = t, даже когда t равно NULL, иначе ваш цикл не завершится (но удаление одного и того же узла несколько раз может вызвать исключение).

        } 
    } 

    void add(node * n) {
        if (!head) {
            head = n;
            head->next = NULL;
            tail = head;
            nodeCount = 0;

Я думаю, что все это утверждение if не нужно. Обе ветви делают одно и то же, когда связанный список пуст, поэтому вы всегда можете использовать вторую ( else ) ветку.

Кроме того, установка nodeCount на 0 в конце кажется ошибкой; не должно ли это быть 1?

        } else {
            node * t = head;

            while (t->next)
                t = t->next;

            t->next = n;
            n->next = NULL;
            nodeCount++;
        } 
    } 

    node * operator[](const int &i) {

Незначительная гнида, но аргумент const int & не имеет никакого смысла. Вы ничего не получите, просто пройдя тип int.

        if ((i >= 0) && (i < nodeCount))
            throw new exception("ERROR:  Invalid index on linked list.", -1);

Вышеуказанное состояние смотрит назад на меня; вы всегда будете выдавать исключение с допустимыми параметрами.

        node *t = head;

        for (int x = i; x < nodeCount; x++)
            t = t->next;

Цикл выше кажется мне немного подозрительным. Если i - это нулевой индекс нужного вам узла, не должен ли ваш счетчик перейти с 0 на i-1?

        return t;
    } 

    void print() {
        if (!head)
            return;

Опять же, вы должны заставить этот метод работать без необходимости защиты от пустого списка. Я ожидаю, что пустой список напечатает что-то вроде [], но с указанным выше защитным кодом вы ничего не напечатаете.

        node * t = head;
        string collection;
        int c = 0;

        cout << "[";

        if (!t->next) {
            cout << *(t->data);

Как и другое условие выше, я думаю, что ветвь выше не нужна. Вторая ( else ) ветвь должна нормально обрабатывать случай NULL.

        } else {
            while (t->next) {

Я думаю, вы хотите while (t) выше, а не while (t->next)

                cout << *(t->data);
                c++;

                if (t->next)
                    t = t->next;

Я думаю, что это ошибка, подобная той, что была ранее. Вы должны всегда назначать `t = t-> next ', иначе ваш цикл не завершится.

                if (c < nodeCount)
                    cout << ", ";
            } 
        } 

        cout << "]" << endl;
    } 
};

int main (const int & argc, const char * argv[]) { // const int& ?

const int & снова ...

    try {
        linkedList * myList = new linkedList;
        for (int x = 0; x < 10; x++)
            myList->add(new node(x));
        myList->print();
    } catch(exception &ex) {
        cout << ex.what() << endl;
        return -1;
    }  

Вероятно, это хорошо для домашнего задания, но перехват всех возможных исключений (как вы делаете выше), вероятно, не приносит особой пользы и обычно считается плохой практикой.

Если вы пропустите эту попытку / уловку выше, я подозреваю, что всякий раз, когда ваша программа завершает работу с исключением, обработчик исключений верхнего уровня по умолчанию, предоставляемый компилятором, будет в любом случае выгружать информацию об исключении и трассировке стека (зависит от вашего компилятор).

    return 0;
}
0 голосов
/ 13 марта 2010

1) Вы можете использовать data = new int(i) для инициализации поля.

2) Вы создали неправильное условие в операторе [] if ((i >= 0) && (i < nodeCount)), оно должно быть инвертировано, как if ((i < 0) || (i >= nodeCount))

0 голосов
/ 13 марта 2010
if ((i >= 0) && (i < nodeCount)) throw new exception("ERROR:  Invalid index on linked list.", -1);

Эта строка должна гласить:

if( i < 0 || i >= nodeCount ) throw...

Метод добавления также имеет проблемы:

void add(node * n){
    if (!head) {
        head = n;
        head->next = NULL;
        tail = head;
        nodeCount = 0;
    }else {
        node * t = head;
        while (t->next) t = t->next;
        t->next = n;
        n->next = NULL;
        nodeCount++;
    }
}

Для одного после добавления счетчик первого узла будет равен 0, для другого вы выполняете поиск для вставки в связанный список ... операция, которую люди ожидают, будет O (1).

Более типичная реализация будет:

void add( node* n ) {
   node* old_head = head;
   head = n;
   head->next = old_head;
   ++nodeCount;
   if( old_head == NULL )
      tail = head
}
...