Почему шаблонный класс(C++) может некорректно работать при одном типе данных, но корректно работать при другом? [дубликат]

107
24 ноября 2021, 14:00
На этот вопрос уже даны ответы здесь:
Правильное использование проверки конца файла (2 ответа)
Закрыт 1 год назад.

Реализовала односвязный список с помощью шаблона класса. В основной программе написала подпрограммы ввода и вывода в файл. При создании списка целых чисел все выводится корректно. Но когда создаю список символов последний элемент выводится дважды. В чем может быть ошибка?

List.h:

template <typename T>
class List {
public:
    List();
    ~List();
    bool IsEmpty();
    void push_front(T data);
    void push_middle(T data);
    void push_back(T data);
    void pop_front();
    void pop_middle();
    void pop_back();
    void removeAt(int index);
    T& get_first();
    T& get_last();
    T& get_middle();
    void clear();
    int GetSize() const { return Size; }
    T& operator[](const int index) const ;
private:
    template<typename U>
    class Node
    {
    public:
        Node * pNext;
        U data;
        Node(U data = U(), Node *pNext = nullptr)
        {
            this->data = data;
            this->pNext = pNext;
        }
    };
    int Size;
    Node<T> *head;
};

List.cpp:

#include "List.h"
#include <iostream>
using namespace std;
template<typename T>
List<T>::List()
{
    Size = 0;
    head = nullptr;
}

template<typename T>
List<T>::~List(){
    clear();
}

template<typename T>
bool List<T>::IsEmpty() {
    return ( head == nullptr );
}

template<typename T>
void List<T>::pop_front(){
    Node<T> *temp = head;
    head = head->pNext;
    delete temp;
    Size--;
    return;
}
template<typename T>
void List<T>::push_back(T data){
    if (head == nullptr){
        head = new Node<T>(data);
    }
    else{
        Node<T> *current = this->head;
        while (current->pNext != nullptr){
            current = current->pNext;
        }
        current->pNext = new Node<T>(data);
    }
    Size++;
    return;
}
template<typename T>
void List<T>::clear(){
    while (Size){
        pop_front();
    }
    return;
}

template<typename T>
T & List<T>::operator[](const int index) const
{
    int counter = 0;
    Node<T> *current = this->head;
    while (current != nullptr)
    {
        if (counter == index)
        {
            return current->data;
        }
        current = current->pNext;
        counter++;
    }
}
template<typename T>
void List<T>::push_front(T data){
    head = new Node<T>(data, head);
    Size++;
    return;
}
template<typename T>
void List<T>::push_middle(T data){
    if (this->Size == 1){
        push_front(data);
    }
    else{
        Node<T> *previous = this->head;
        for (int i = 0; i < this->Size/2 - 1; i++){
            previous = previous->pNext;
        }
        auto *newNode = new Node<T>(data, previous->pNext);
        previous->pNext = newNode;
        Size++;
    }
    return;
}
template<typename T>
void List<T>::removeAt(int index){
    if (index == 0){
        pop_front();
    }
    else{
        Node<T> *previous = this->head;
        for (int i = 0; i < index - 1; i++){
            previous = previous->pNext;
        }
        Node<T> *toDelete = previous->pNext;
        previous->pNext = toDelete->pNext;
        delete toDelete;
        Size--;
    }
    return;;
}

template<typename T>
void List<T>::pop_middle(){
    if (Size == 1){
        pop_front();
    }
    else{
        Node<T> *previous = this->head;
        for (int i = 0; i < Size/2 - 1; i++){
            previous = previous->pNext;
        }
        Node<T> *toDelete = previous->pNext;
        previous->pNext = toDelete->pNext;
        delete toDelete;
        Size--;
    }
    return;
}

template<typename T>
void List<T>::pop_back(){
    if (Size == 1){
        pop_front();
    }
    else{
        Node<T> *previous = this->head;
        for (int i = 0; i < Size - 2; i++){
            previous = previous->pNext;
        }
        Node<T> *toDelete = previous->pNext;
        previous->pNext = toDelete->pNext;
        delete toDelete;
        Size--;
    }
    return;
}
template<typename T>
T &List<T>::get_first() {
    return head->data;
}
template<typename T>
T &List<T>::get_last() {
   if(Size==1){
      get_first();
   }
   else {
       Node<T> *previous = this->head;
       for (int i = 0; i < Size - 1; i++) {
           previous = previous->pNext;
       }
       return previous->data;
   }
}
template<typename T>
T &List<T>::get_middle() {
    if(Size==1){
        get_first();
    }
    else {
        Node<T> *previous = this->head;
        for (int i = 0; i < Size/2; i++) {
            previous = previous->pNext;
        }
        return previous->data;
    }
}

Основная программа:

#include "List.h"
using namespace std;
template <typename T>
void InputList(List<T>& lst, char* fname){
    fstream file;
    file.open(fname,ios_base::in);
    if (!file.is_open()){
       cout<<"Невозможно открыть файл "<< fname <<"\n";
    }
    else{
        while(!file.eof()){
            T x;
            file >> x;
            lst.push_back(x);
        }
    }
    file.close();
    return;
}
template <typename T>
void OutputList(const List<T>& lst, char* fname){
    fstream file;
    file.open(fname,ios_base::out);
    if (!file.is_open()){
        cout<<"Невозможно открыть файл "<< fname <<"\n";
    }
    else{
        for(int i = 0; i < lst.GetSize(); ++i){
            file << lst[i] <<" ";
        }
    }
    file<<"\n";
    file.close();
    return;
}
int main(int argc, char * argv[]) {
    List< char > lst1;
    List< int > lst2;
    InputList(lst1,argv[1]);
    InputList(lst2,argv[2]);
    OutputList(lst1,argv[4]);
    OutputList(lst2,argv[5]);
    return 0;
}
Answer 1

Почему вы вдруг решили, что виноват "шаблонный класс"? Шаблонный класс тут совершенно ни при чем. Что вы сами в него положили, то там и лежит. Ошибок в вашем шаблоне хватает, но я думаю, что виноват в данном случае не он.

(И, кстати, как это вам удалось сделать определения методов шаблона в .cpp файле?)

Последний элемент выводится дважды потому, что вы сами его занесли в список дважды. Вот он и выводится дважды.

Будете писать такие циклы

while(!file.eof()){

будете получать такие "ошибки". В вашей программе не должно быть циклов по eof(). Это практически всегда ошибка.

См. Правильное использование проверки конца файла

Answer 2

Это тот случай, когда правильная запись компактнее и читабельней:

template <typename T>
void InputList(List<T>& lst, char* fname){
    //...
    else{
        T x;
        while(file >> x;)                
            lst.push_back(x);
    }
    //...
}
READ ALSO
Задача на массивы C++ [закрыт]

Задача на массивы C++ [закрыт]

Хотите улучшить этот вопрос? Добавьте больше подробностей и уточните проблему, отредактировав это сообщение

93
Ошибка записи в память?

Ошибка записи в память?

https://pastebincom/Cz7Dti3N Полный рабочий кусок

250
Как решить эту задачу используя указатели?(по массиву можно двигаться только с помощью них)

Как решить эту задачу используя указатели?(по массиву можно двигаться только с помощью них)

Дано натуральное число n (<= 100)Получить все простые числа, не превышающие это значение

195
Как получить доступ к элементам динамического массива по ссылке?

Как получить доступ к элементам динамического массива по ссылке?

Есть динамический массив объектов, как передать его в функцию и получить доступ к отдельному элементу?

165