Нужна помощь с конструктором копирования для очень простой реализации односвязных списков - PullRequest
1 голос
/ 16 марта 2011

На прошлой неделе мы создали программу, которая управляет наборами строк, используя классы и векторы. Я смог выполнить это 100%. На этой неделе мы должны заменить вектор, который мы использовали для хранения строк в нашем классе, простыми односвязными списками.

Функция в основном позволяет пользователям объявлять наборы строк, которые являются пустыми, и наборы только с одним элементом. В главном файле есть вектор, элементами которого являются структура, содержащая setName и strSet (class).

ЗДЕСЬ МОЯ ПРОБЛЕМА: Он имеет дело с конструктором копирования класса. Когда я удаляю / закомментирую конструктор копирования, я могу объявить столько пустых или одиночных наборов, сколько захочу, и вывести их значения без проблем. Но я знаю, что мне, очевидно, понадобится конструктор копирования для реализации остальной части программы. Когда я оставляю конструктор копирования внутри, я могу объявить один набор, один или пустой, и вывести его значение. Но если я объявляю второй набор и пытаюсь вывести один из первых двух наборов, я получаю ошибку сегментации. Более того, если я пытаюсь объявить более двух наборов, я получаю ошибку сегментации. Любая помощь будет оценена !!

Вот мой код для очень простой реализации всего:

Вот setcalc.cpp: (основной файл)

#include <iostream>
#include <cctype>
#include <cstring>
#include <string>
#include "strset2.h"

using namespace std;

// Declares of structure to hold all the sets defined
struct setsOfStr {
    string nameOfSet;
    strSet stringSet;
};

// Checks if the set name inputted is unique
bool isSetNameUnique( vector<setsOfStr> strSetArr, string setName) {
    for(unsigned int i = 0; i < strSetArr.size(); i++) {
        if( strSetArr[i].nameOfSet == setName ) {
            return false;
        }
    }
    return true;
}

int main() {
    char commandChoice;
    // Declares a vector with our declared structure as the type
    vector<setsOfStr> strSetVec;

    string setName;
    string singleEle;
    // Sets a loop that will constantly ask for a command until 'q' is typed
    while (1) {
            cin >> commandChoice;
        // declaring a set to be empty
        if(commandChoice == 'd') {
            cin >> setName;
            // Check that the set name inputted is unique
            if (isSetNameUnique(strSetVec, setName)  == true) {
                strSet emptyStrSet;
                setsOfStr set1;
                set1.nameOfSet = setName;
                set1.stringSet = emptyStrSet;
                strSetVec.push_back(set1);
            }
            else {
                cerr << "ERROR: Re-declaration of set '" << setName << "'\n";
            }
        }
        // declaring a set to be a singleton
        else if(commandChoice == 's') {
            cin >> setName;
            cin >> singleEle;
            // Check that the set name inputted is unique
            if (isSetNameUnique(strSetVec, setName) == true) {
                strSet singleStrSet(singleEle);
                setsOfStr set2;
                set2.nameOfSet = setName;
                set2.stringSet = singleStrSet;
                strSetVec.push_back(set2);
            }
            else {
                cerr << "ERROR: Re-declaration of set '" << setName << "'\n";
            }
        }
        // using the output function
        else if(commandChoice == 'o') {
            cin >> setName;
            if(isSetNameUnique(strSetVec, setName) == false) {
                // loop through until the set name is matched and call output on its strSet
                for(unsigned int k = 0; k < strSetVec.size(); k++) {
                    if( strSetVec[k].nameOfSet == setName ) {
                            (strSetVec[k].stringSet).output();
                    }
                }
            }
            else {
                cerr << "ERROR: No such set '" << setName << "'\n";
            }
        }
        // quitting
        else if(commandChoice == 'q') {
            break;
        }
        else {
            cerr << "ERROR: Ignoring bad command: '" << commandChoice << "'\n";
        }
    }
    return 0;
}

Вот strSet2.h:

#ifndef _STRSET_
#define _STRSET_

#include <iostream>
#include <vector>
#include <string>

struct node {
    std::string s1;
    node * next;
};

class strSet {

private:
    node * first;
public:
    strSet ();  // Create empty set
    strSet (std::string s); // Create singleton set
    strSet (const strSet &copy); // Copy constructor
    // will implement destructor and overloaded assignment operator later

    void output() const;


};  // End of strSet class

#endif  // _STRSET_

А вот и strSet2.cpp (реализация класса)

#include <iostream>
#include <vector>
#include <string>
#include "strset2.h"

using namespace std;

strSet::strSet() {
    first = NULL;
}

strSet::strSet(string s) {
    node *temp;
    temp = new node;
    temp->s1 = s;
    temp->next = NULL;
    first = temp;
}

strSet::strSet(const strSet& copy) {
    cout << "copy-cst\n";
    node *n = copy.first;
    node *prev = NULL;
    while (n) {
        node *newNode = new node;
        newNode->s1 = n->s1;
        newNode->next = NULL;
        if (prev) {
            prev->next = newNode;
        }
        else {
            first = newNode;
        }
        prev = newNode;
        n = n->next;
    }
}

void strSet::output() const {
    if(first == NULL) {
        cout << "Empty set\n";
    }
    else {
        node *temp;
        temp = first;
        while(1) {
            cout << temp->s1 << endl;
            if(temp->next == NULL) break;
            temp = temp->next;
        }
    }
}

Ответы [ 3 ]

1 голос
/ 16 марта 2011

Стандарт C ++ гласит, что типы, используемые в стандартном контейнере (например, std :: vector), должны быть копируемыми и назначаемыми.

Поскольку вы не реализовали пользовательский оператор присваивания в классе strSet,компилятор сгенерирует один для вас, который делает простое членское копирование.В вашем случае это означает, что первый указатель будет скопирован напрямую.Очевидно, это означает, что два объекта теперь «владеют» узлами в наборе, и вы получите сбой при его двойном освобождении.

Некоторые советы:

  1. Реализацияпользовательский оператор присваивания, который делает то же самое, что и ваш конструктор копирования

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

например,

bool isSetNameUnique (const vector & strSetArr, const string & setName)

Удачи:)

0 голосов
/ 16 марта 2011

это выглядит немного странно:

strSet::strSet(string s) {
    node *temp;
    temp = new node;
    temp->s1 = s;
    temp->next = NULL;
    first = temp;
}

что, если 'first' уже указывает на что-то?Затем вы фактически убиваете предыдущий список и вызываете утечку памяти.

0 голосов
/ 16 марта 2011

Ваш strSet конструктор копирования не назначает член first, когда его аргумент пуст.Это приводит к неопределенному поведению.

Кроме того, оператор присваивания strSet (operator=), который был показан до редактирования, был определенно неверным;и это действительно не очень хорошая идея определить конструктор копирования, но позволить неявно определять деструктор и оператор присваивания компилятором.См. Правило трех .

Один из распространенных способов реализации Большой тройки, когда им требуется значительное управление (как в этом случае), выглядит примерно так:

...