Песня повторение менеджер со стеком


Упражнение было взять файле, например это:

You say yes
I say no
You say stop
And I say go go go
CHORUS:
Oh no
You say Goodbye
And I say hello
Hello hello
I don't know why
You say Goodbye
I say hello
#repeat 9 12
Why
#repeat 11 13
I say high
You say low
You say why
And I say I don't know
#repeat 5 19

И анализ повторяется, так что они фактически повторяют линии. Вложенные повторы должны работать, и мне пришлось использовать мой собственный класс стека.

main.cpp

#include "Stack.h"
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

void parseLines(const std::vector<std::string> text, const int begin, const int end);
void parseRepeat(const std::string repeatLine, int &begin, int &end);


int main() {
    int fileLines = 0;

    std::string in;
    std::string fileName = "lyrics";
    std::ifstream inFile(fileName.c_str());
    std::vector<std::string> text;

    while(!inFile.eof()) {
        getline(inFile, in);
        text.push_back(in);
        fileLines++;
    }

    parseLines(text, 0, fileLines);
}

void parseLines(const std::vector<std::string> text, const int begin, const int end) {
    Stack<int> lyricStack;

    int fromLine = begin;
    int toLine = end;


    while(fromLine <= toLine && (unsigned int)fromLine < text.size()) {
        if(text.at(fromLine).substr(0, 7) == "#repeat") {
            lyricStack.push(fromLine);
            lyricStack.push(toLine);

            parseRepeat(text.at(fromLine), fromLine, toLine);
        } else {
            std::cout << text.at(fromLine) << std::endl;
            fromLine++;
        }

    }


    while(!lyricStack.isEmpty()) {
        toLine = lyricStack.pop();
        fromLine = lyricStack.pop();

        fromLine++;

        parseLines(text, fromLine, toLine);
    }
}

void parseRepeat(const std::string repeatLine, int &begin, int &end) {
    std::string numbers = repeatLine.substr(repeatLine.find(" ") + 1);
    begin = atoi((numbers.substr(0, numbers.find(" "))).c_str()) - 1;
    end = atoi((numbers.substr(numbers.find(" "))).c_str()) - 1;
}

Стек.ч

#ifndef STACK_H_
#define STACK_H_

#include <string>

template <class A>
struct NODE {
    A data;
    NODE* next;
};

template <class B>
class Stack {
private:
    NODE<B>* head;

public:
    Stack();
    virtual ~Stack();

    void push(const B item);
    B pop();
    bool isEmpty();
};

#endif /* STACK_H_ */

Stack.cpp

#include "Stack.h"
#include <string>

template <class B>
Stack<B>::Stack() {
    head = NULL;
}

template <class B>
Stack<B>::~Stack() {
    NODE<B>* current = head;
    NODE<B>* previous;

    while(current) {
        previous = current;
        current = current->next;
        delete previous;
    }
}

template <class B>
void Stack<B>::push(const B item) {
    NODE<B>* newNode = new NODE<B>;
    newNode->data = item;

    if(!head) {
        newNode->next = NULL;
        head = newNode;
    } else {
        newNode->next = head;
        head = newNode;
    }
}

template <class B>
B Stack<B>::pop() {
    NODE<B>* next = head->next;

    B data = head->data;

    delete head;

    head = next;

    return data;
}

template <class B>
bool Stack<B>::isEmpty() {
    if(!head) {
        return true;
    }

    return false;
}


436
7
задан 23 ноября 2011 в 03:11 Источник Поделиться
Комментарии
1 ответ

Первая основная проблема-код не компилируется:

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

main.cpp: Uses Stack<int> thus needs the constructor and destructor.
But does not have them at this point. So waits for the linker to find one.

Stack.cpp: Contains only template definition and thus does **NOT** generate any code.
Templates only become real code when there is an instantiation
(implicit as done by the compiler in main.cpp or explicit when you do it).
Since there is no instantiation there is no code.

Все это означает, что главное заполнить не ссылка.

Самый простой способ решить эту проблему:


  1. Переименовать Stack.cpp для стека.ТПП

  2. #включить "стека.ТПП" изнутри "стопку.ч"

Причина переименовать в стек.ТЭС заключается в том, что некоторые системы сборки будут пытаться построить все исходные и так как это включено в заголовок это может вызвать проблемы в некоторых системах построения. Поэтому лучше изменить название, чтобы избежать проблем в будущем. ТЭЦ часто используется для хранения шаблонов определений.

Никогда не делайте этого:

while(!inFile.eof()) {
getline(inFile, in);
text.push_back(in);
fileLines++;
}

Несколько проблем:


  1. Если Гэтлину() выдает ошибки (не ВФ) в итоге вы получите бесконечный цикл.

  2. В последней строке читать чтения до (но не мимо ВФ).
    Итак, вы прочитали последнюю строчку, но ВФ не установлено. Вы повторно входим в цикл, попытаться получить другую строку, это не удается, и устанавливает ВФ, но ничего не происходит в пока вы еще нажать на текст. Так что вы в конечном итоге толкает дважды последнюю строчку.

Правильный способ чтения из файла:

while(getline(inFile, in))
{
// The loop is only entered if getline() successfully reads a line.
// Thus it solves the two problems above.

text.push_back(in);
fileLines++;
}

Это работает, потому что вызовом getLine() возвращает ссылку на поток. Когда поток используется в логическом контексте оно преобразуется к типу, который может быть использован как логический (типа неуказанных в C++03, но типа bool в C++11). Он преобразуется путем проверки не (флаг). Если это верно, преобразование возвращает объект эквивалентен false в противном случае объект эквивалентно True.

При передаче больших объектов в качестве параметров рассмотрим передавая их по ссылке. Если они никогда не изменяются проходите мимо const ссылка. Если вы не компилятор будет генерировать копию.

void parseLines(const std::vector<std::string> text, const int begin, const int end) {

Здесь вы должны, вероятно, передать текст по константной ссылке.

void parseLines(const std::vector<std::string> const& text, const int begin, const int end) {
// ^^^^^^^^

То же самое и здесь:

void parseRepeat(const std::string repeatLine, int &begin, int &end) {
// Should probably be
void parseRepeat(const std::string& repeatLine, int &begin, int &end) {
// ^^^

Кастинг-это почти всегда признак того, что что-то неправильно с вашим дизайном:

(unsigned int)fromLine

Это должен быть намек на то, что номерстроки показывает неправильный тип. Так как это измерение размера и не менее чем на 0, это свидетельствует о том, что правильный тип для типа unsigned int (или, вероятно, представляют собой типы size_t).

void parseLines(const std::vector<std::string> text, std::size_t const begin, std::size_t const end) {
Stack<int> lyricStack;

std::size_t fromLine = begin;
std::size_t toLine = end;

while(fromLine <= toLine && fromLine < text.size()) {

ятвой() быстрый, но нестандартный и не всегда доступны. Наиболее простой способ-это просто использовать операторы потока. Это всегда будет работать, и 99% времени будет достаточно быстро. Только оптимизировать, когда вы знаете, что это делает разницу.

Также этот код настолько плотная, он практически нечитаем. Белое пространство-это ваш друг.

void parseRepeat(const std::string repeatLine, int &begin, int &end)
{
std::string numbers = repeatLine.substr(repeatLine.find(" ") + 1);
begin = atoi((numbers.substr(0, numbers.find(" "))).c_str()) - 1;
end = atoi((numbers.substr(numbers.find(" "))).c_str()) - 1;
}

Гораздо легче писать с потоками:

void parseRepeat(const std::string repeatLine, int &begin, int &end)
{
// This line always has the form
// #repeat <number> <number>
std::stringstream linestream(repeatLine);
std::string repeat;

linestream >> repeat >> begin >> end;
}

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

В этой ситуации узел будет легко реализовать с помощью смарт-указатель.

Также вы перемещающих внутри узла стека. Это упрощает конструкцию, если вы переместите эту работу конструктору узла. Это делает код в стек гораздо легче читать, так как он просто обрабатывает стек и узел может справиться сам.

Стек является, возможно, контейнер, поэтому она может иметь указатель, но вы должны выполнить "правило трех". Это означает, что необходимо реализовать конструктор копирования/оператор присваивания/деструктор или сделать эти методы частные. Причина в том, что если этого не сделать, то компилятор будет автоматически генерировать эти методы. И генерируемый компилятором версии не плат с "принадлежит сырые указатели".

{
Stack<int> a;
a.push(4);

Stack<int> b(a); // Copy made of a

DO STUFF
} // Here B is destroyed.
// But because you did not define a copy constructor b is a shallow copy of a
// This means they both contain the same pointer. So b deletes it first.
// Now A is destroyed.
// This also deletes its pointer (the same as b) but it has already been deleted.
// Your program is now free to produce nasal daemons (undefined behavior).

Самым простым решением является отключение копирования и оператор присваивания.

У вас есть более сложные толчок:

if(!head) {
newNode->next = NULL;
head = newNode;
} else {
newNode->next = head;
head = newNode;
}

Не проще ли написать так:

newNode->next = head;
head = newNode;

Также isEmpty() является более сложным:

if(!head) {
return true;
}

return false;

Может быть записано как:

return head == NULL;

12
ответ дан 23 ноября 2011 в 04:11 Источник Поделиться