0

我使用了两种设计模式 Composite 和 Visitor。我对 Composite 没有任何问题。但是当他开始为输入和输出编写派生类时,出现了一些错误,我从未找到过解决方案。尽管在现实中,如果一切都是InputVisitor完成的,那么只剩下PrintVisitormain()

这是我的代码:

UPD:我重写了部分代码。现在我没有错误,但它只打开终端,什么也没做......

PS也许我没有正确实施模式。有人有更好的主意吗?

#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;
}
4

3 回答 3

0

第一个问题。

假设错字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;
}

这会取消引用未初始化的指针,从而产生Undefined Behavior

不要那样做。

将其重写为例如

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);

where_outputFile是 type 的成员ofstream,并且outputFile是 a string

这使用了 g++ 在 4.7.1 版中未实现的 C++11 功能(流的右值分配)。

大概的想法是关闭_outputFile并重新打开它:只需这样做,代码将更具可移植性:

// Add failure checking:
_outputFile.close();
_outputFile.open( outputFile );

此外,更具描述性的命名会更好……

给定代码中的一般问题集。

编译上面的代码会产生一些关于有符号/无符号比较和未使用的局部变量的警告。解决这一切。-Wall使用 g++ 和Visual C++以最高实际警告级别构建/W4,并使其编译干净。

逻辑错误和测试。

即使它编译干净,也可能并且通常会保留逻辑错误。要根除这些问题,请设计一组完全可重复的测试,并附上任何必要的测试数据。

于 2013-01-19T12:45:55.427 回答
0

在这段代码中:

InputVisitor *input;
input->setFile("root.txt");
root->accept(input);  //Here

InputVisitor 是一个*输入。您的接受函数引用访问者。将其更改为:

root->accept(*input);  //Here

除了我在评论中提到的错字之外,一切都会好起来的。

如果你删除了class类名前面的所有,编译器会告诉你什么时候有拼写错误的类名。否则,它只会将其视为“将来某个时候会有一个名为的类Visirot,而我现在并不关心其中有什么。

于 2013-01-19T12:35:46.720 回答
0

这是 的声明BaseComponent::accept

virtual void accept(class Visirot &)=0;

这是电话:

root->accept(input);  //Here

这是 的声明input

InputVisitor *input;

所以,首先,声明BaseComponent::accept显然有一个错字;Visirot应该是Visitor

其次,accept需要 a Visitor&,但它是用 a 调用的InputVisitor*InputVisitor派生自Visitor, soInputVisitor* is convertible toVisitor* andInputVisitor& is convertible toVisitor& , but there is no conversion from a pointer-to-type into a reference-to-type. So either changeBaseComponent::accept` 以获取指针或更改调用以传递引用。

于 2013-01-19T12:36:07.980 回答