C++を知りたい(6) OOP Wikiを作る
2009/07/23
C++のOOPの勉強をWikiを作りながら行きあたりばったりで進めていますが、大量のコンパイルエラーに何度もめげそうになりました。単純な多態が機能しなくて調べていたところ、下の説明を見付けました。
多態性は、メンバ関数がオーバーライドされており、スーパークラス型の変数がポインタ(または 第15章で登場する「参照」)である必要があります。
C++編(言語解説) 第9章 オーバーライド
これにはかなり驚かされたんですが、C++では常識なんですね。
練習で作っているWiki
(後日説明を追加する予定)
Elementがwikiの文法の1行を表すクラス。Elementを継承したHeadline(見出し)とItem(箇条書き)クラスを定義しました。機能が乏しい状態であるにも関らず、既に泥臭い部分が多いです。まだまだ途中ですが是非添削おねがいします。今後ももうすこしの間は、機能追加をしながらC++のOOPを習得したいと思います。
wiki.hh
#include <vector> using namespace std; /** * 要素 */ class Element { protected: string line; public: string name; Element(){}; Element(string l); virtual ~Element(); virtual string toHtml(); virtual string before(); virtual string after(); }; /** * 見出し */ class Headline : public Element { public: Headline(string l); ~Headline(); string toHtml(); string before(); string after(); }; /** * 箇条書き */ class Item : public Element { public: Item(string l); ~Item(); string toHtml(); string before(); string after(); }; /** * wikiのページを表すクラス */ class Wiki { private: vector<Element*> elements; string id; public: Wiki(string id); ~Wiki(); void output(); }; /** * 文法を判定して適切な要素を返す。 */ Element* elementFactory(string line); /* vim: set ts=4 sw=4 sts=4 expandtab fenc=utf-8: */
wiki.cc
#include <vector> #include <iostream> #include <fstream> #include <sstream> #include "wiki.hh" using namespace std; /** * 要素実装 */ Element::Element(string l) { line = l; name = "Element"; } Element::~Element() { } string Element::before() { string s; return s; } string Element::after() { string s; return s; } string Element::toHtml() { ostringstream html; html << "<p>" << line << "</p>" << endl; return html.str(); } /** * 見出し実装 */ Headline::Headline(string l) { line = l; name = "Headline"; } Headline::~Headline() { } string Headline::before() { string s; return s; } string Headline::after() { string s; return s; } string Headline::toHtml() { ostringstream html; html << "<h1>" << line.substr(1) << "</h1>" << endl; return html.str(); } /** * 箇条書き実装 */ Item::Item(string l) { line = l; name = "Item"; } Item::~Item() { } string Item::before() { string s("<ul>\n"); return s; } string Item::after() { string s("</ul>\n"); return s; } string Item::toHtml() { ostringstream html; html << "\t<li>" << line.substr(1) << "</li>" << endl; return html.str(); } /** * Wiki実装 */ Wiki::Wiki(string arg_id) { id = arg_id; ostringstream filename_stream; filename_stream << id << ".txt"; ifstream ifs(filename_stream.str().c_str()); string buf; while (ifs && getline(ifs, buf)) { elements.push_back(elementFactory(buf)); } } Wiki::~Wiki() { unsigned int size = elements.size(); for (unsigned int i = 0; i < size; i++) { delete elements[i]; } } void Wiki::output() { cout << "Content-Type: text/html\n\n"; cout << "<html><body>\n"; unsigned int size = elements.size(); for (unsigned int i = 0; i < size; i++) { if (i > 0 && elements[i - 1]->name != elements[i]->name) { // 前の要素と異なる場合は、beforeメソッドを呼び出す。 cout << elements[i]->before(); } cout << elements[i]->toHtml(); if (i < size - 1 && elements[i]->name != elements[i + 1]->name) { // 次の要素と異なる場合は、afterメソッドを呼び出す。 cout << elements[i]->after(); } } cout << "</body></html>\n"; } Element* elementFactory(string line) { if (line.at(0) == '*') { Headline* h = new Headline(line); return h; } else if (line.at(0) == '-') { Item* i = new Item(line); return i; } else { Element* e = new Element(line); return e; } } /* vim: set ts=4 sw=4 sts=4 expandtab fenc=utf-8: */
htmlエスケープもまだ実装してません。
index.cc
#include <rude/cgi.h> #include "wiki.hh" using namespace std; int main() { rude::CGI cgi; string id(cgi["id"]); Wiki wiki(id); wiki.output(); return 0; } /* vim: set ts=4 sw=4 sts=4 expandtab fenc=utf-8: */
Makefile
default: index.cgi wiki.o: wiki.cc wiki.hh g++ -Wall -c -o wiki.o wiki.cc index.o: index.cc g++ -Wall -c -o index.o index.cc index.cgi: wiki.o index.o g++ -o index.cgi index.o wiki.o -lrudecgi
data.txt
*見出し 普通のパラグラフ -箇条書き1 -箇条書き2 -箇条書き3 普通のパラグラフ おわり。
実行結果
<html><body> <h1>見出し</h1> <p>普通のパラグラフ</p> <ul> <li>箇条書き1</li> <li>箇条書き2</li> <li>箇条書き3</li> </ul> <p>普通のパラグラフ</p> <p>おわり。</p> </body></html>