Я использовал два дизайна шаблона Composite и Visitor. У меня нет проблем с Composite. Но когда он начал писать производные классы для ввода и вывода, получилось несколько ошибок, решение которых я так и не нашел. Хотя на самом деле и если все будет InputVisitor сделано так осталось только PrintVisitor а также главный().
Вот мой код:
UPD: Я переписываю некоторые части кода. Теперь у меня нет ошибок, но он только открывает терминал и ничего не делает …
Постскриптум Возможно я не правильно реализовал шаблоны. У кого-то есть идея получше?
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
class BaseComponent {
public:
virtual void add(BaseComponent *)=0;
virtual void accept(class Visitor &)=0;
};
class Card :public BaseComponent {
public:
Card (string bookName, vector<string> authors, int year): _bookName(bookName), _authors(authors), _year(year) {}
string getBookName() const {
return _bookName;
}
vector<string> getAuthors() const {
return _authors;
}
int getYear() const {
return _year;
}
void setBookName(string bookName) {
_bookName = bookName;
}
void setAuthors(vector<string> authors) {
copy(authors.begin(), authors.end(), _authors.begin());
}
void setYear(int year) {
_year = year;
}
void add(BaseComponent *){}
void accept(class Visitor &);
private:
string _bookName;
vector<string> _authors;
int _year;
};
class Folder :public BaseComponent {
public:
Folder(): _folderName(""), _parentFolder("") {}
Folder(string parentFolder): _folderName(""), _parentFolder(parentFolder) {}
string getFolderName() const {
return _folderName;
}
string getParentName() const {
return _parentFolder;
}
vector<BaseComponent*> getSubFolders() const {
return _subFolders;
}
void setFolderName(string folderName) {
_folderName = folderName;
}
void setParentFolder(string parentFolder) {
_parentFolder = parentFolder;
}
void add(BaseComponent *component) {
_subFolders.push_back(component);
}
void accept(class Visitor &);
private:
string _folderName;
string _parentFolder;
vector<BaseComponent*> _subFolders;
};
class Visitor {
public:
virtual void visitCard(Card *)=0;
virtual void visitFolder(Folder *)=0;
};
void Card::accept(class Visitor &visitor) {
visitor.visitCard(this);
}
void Folder::accept(class Visitor &visitor) {
visitor.visitFolder(this);
}
class InputVisitor :public Visitor {
public:
InputVisitor(string file): _file(file){}
void setFile(string file) {
_file = file;
}
void visitCard(Card *){}
void visitFolder(Folder *folder){
ifstream input(_file);
string folderName;
getline(input, folderName);
folder->setFolderName(folderName);
string fileName;
while (!input.eof()) {
input >> fileName;
if (fileName == "----") {
break;
} else {
Folder *subFolder = new Folder(folderName);
InputVisitor *inputVisitor = new InputVisitor(fileName);
subFolder->accept(*inputVisitor);
folder->add(subFolder);
}
}
while (!input.eof()) {
string name, tempAuthor;
vector<string> authors;
int n, year;
input >> name;
input >> n;
for (int i = 0; i<n; ++i) {
input >> tempAuthor;
authors.push_back(tempAuthor);
}
input >> year;
Card *subBook = new Card(name, authors, year);
folder->add(subBook);
}
input.close();
}
private:
string _file;
};
class PrintVisitor :public Visitor {
public:
PrintVisitor(string outputFile): _outputFile(outputFile) {}
void setOutputFile(string outputFile) {
_outputFile = outputFile;
}
void visitFolder(Folder *folder) {
ofstream output(_outputFile);
output << folder->getFolderName() << endl << "\t";
vector<BaseComponent*> subFolders = folder->getSubFolders();
vector<BaseComponent*>::iterator it;
for (it=subFolders.begin(); it!=subFolders.end(); ++it) {
(*it)->accept(*this);
}
output.close();
}
void visitCard(Card *card) {
ofstream output(_outputFile);
output << "Book: " << card->getBookName() << endl
<< "Author(s): ";
vector<string> authors = card->getAuthors();
for (vector<string>::iterator it=authors.begin(); it!=authors.end(); ++it) {
output << *it << " ";
}
output << endl << "Year: " << card->getYear();
output.close();
}
private:
string _outputFile;
};int main() {
Folder root;
root.accept(*(new InputVisitor("root.txt")));
root.accept(*(new PrintVisitor("output.txt")));
return 0;
}
В этом коде:
InputVisitor *input;
input->setFile("root.txt");
root->accept(input); //Here
InputVisitor является * входом. Ваша функция принятия принимает ссылку на посетителя. Измените это на:
root->accept(*input); //Here
и все будет хорошо, кроме опечатки, которую я упомянул в комментарии.
Если вы удалите все class
перед именами для классов компилятор сообщит вам, когда есть имя класса с ошибкой. В противном случае он будет воспринимать это как «класс с именем Visirot
в какой-то момент в будущем, и мне все равно, что в этом сейчас.
Вот декларация BaseComponent::accept
:
virtual void accept(class Visirot &)=0;
Вот звонок:
root->accept(input); //Here
Вот декларация input
:
InputVisitor *input;
Итак, во-первых, декларация BaseComponent::accept
по-видимому, опечатка; Visirot
должно быть Visitor
,
Во-вторых, accept
занимает Visitor&
, но это вызывается с InputVisitor*
, InputVisitor
происходит от Visitor, so
InputVisitor *is convertible to
Посетитель *and
InputVisitor&is convertible to
посетитель&, but there is no conversion from a pointer-to-type into a reference-to-type. So either change
BaseComponent :: accept` для получения указателя или изменения вызова для передачи ссылки.
Предполагая опечатку Visirot
фиксируется, то в строке 152 у вас есть
subFolders[i]->accept(this); //Here some probrem
который передает указатель на T, где ожидается T.
Просто разыщите этот указатель:
subFolders[i]->accept(*this);
Применение тех же исправлений в основной программе будет неправильно:
int main() {
BaseComponent *root;
InputVisitor *input;
input->setFile("root.txt");
root->accept(*input); //Here
PrintVisitor *output;
output->setOutputFile("output.txt");
root->accept(output); //And here
return 0;
}
Это разыменовывает неинициализированные указатели, что приводит к Неопределенное поведение.
Не делай этого.
Перепишите его, например, как
int main() {
Folder root;
InputVisitor input;
input.setFile("root.txt");
root.accept(input); //Here
PrintVisitor output;
output.setOutputFile("output.txt");
root.accept(output); //And here
}
(и, возможно, заменить Folder
с любым конкретным классом вы хотите там.)
В строке 144 вы назначаете потоку:
_outputFile = ofstream(outputFile);
где _outputFile
является членом типа ofstream
, а также outputFile
это string
,
При этом используется функция C ++ 11 (присваивание rvalue потоков), которую g ++ не реализовала в версии 4.7.1.
Предположительно, идея заключается в том, чтобы закрыть _outputFile
и снова откройте его: просто сделайте это, и код будет гораздо более переносимым:
// Add failure checking:
_outputFile.close();
_outputFile.open( outputFile );
Кроме того, было бы неплохо более наглядное именование …
Компиляция выше приводит к ряду предупреждений о сравнении со знаком и без знака и неиспользуемых локальных переменных. Исправить все это. Построить на самом высоком уровне практического предупреждения, -Wall
с g ++ и /W4
с Visual C ++, и сделайте его аккуратным.
Даже когда он компилируется чисто, логические ошибки могут и всегда будут оставаться. Чтобы искоренить их, разработайте набор точно воспроизводимый тесты, дополненные необходимыми данными испытаний.