特定の書籍に関する質問で申し訳ございませんが、宜しくお願いします。
More effective C++ 新訂第3版、項目9、25を読んでいて分からないことがあります
基底クラスに純仮想関数を定義して、派生クラスでインスタンスを生成するという
内容なのですが、仕組みは大体理解できましたが、一点引っかかる事項があります
より抽象化する意味合いだと思うのですが、入力ストリームを、ディスクに限らず、
ネットワークを介してのテープなどでも扱えるように(シリアル化って言うんでしたっ
け?)
一行読み込みの関数の、引数をstd::istream クラスで受けています、経験一行づつの、
ファイル読み込みを行う場合、std::istream の派生クラスである、std::ifstream クラ
スは使用するのですが、当該クラスによる、実装の経験がありません、データを読込み、
コンポーネントを生成してそのポインタを返す部分の実装はどのよに書けばいいのでしょ
うか?
// More Effective C++ Clause 25
#include <fstream>
#include <list>
using namespace std;
class NLComponent
{
virtual NLComponent * clone() const = 0 ;
};
class TextBlock : public NLComponent
{
public:
virtual TextBlock* clone() const { return new TextBlock( *this ); }
};
class GraphBlock : public NLComponent
{
public:
virtual GraphBlock* clone() const { return new GraphBlock( *this ); }
};
class NewsLetter
{
public:
NewsLetter( istream& str );
private:
list<NLComponent* > components;
// strから次のNLComponentのデータを読み込み、
// コンポーネントを生成してインスタンスを返す
static NLComponent* readComponent( istream& str );
};
// ここの実装方法が良く分かりません?
NLComponent* NewsLetter::readComponent( istream& str )
{
// こんな感じかなと思い、書いたのですがコンパイルすらできません
char line[256];
memset( line, 0, 256 );
char* fname = C:\\foo.txt;
ifstream ifs;
ifs.open( fname, ios::in );
return str.getline( fname, sizeof( line ) );
}
NewsLetter::NewsLetter( istream& str ) {
while( !str.eof() ) {
// readComponentから返されたポインタを
// コンポーネントリストの最期に追加する
// push_backは、リストの終りに挿入する
// リストメンバ関数である
components.push_back( readComponent( str ) );
}
}
int main()
{
return 0 ;
}
そもそも、ファイルに依存しないように、ifstreamでなくistreamにするのですよね?
と言うことはつまり、そのクラスでは入力に関知しない(ifstreamは使わない)はずです
が、
なぜ勝手にそこでifstreamのインスタンスを作るのですか?
それは別の人がやって、その結果をistreamで渡してもらうからこそ、汎用的になるので
す。
また、そもそも、std::stringからNLComponent*への変換もできないと思いますし、
NLComponentやcloneからしても明らかに動的生成が想定されるわけで、
本当に提示範囲でコードを書いているならば、根本的に理解が間違っていませんか。
# 訂正
> また、そもそも、std::stringからNLComponent*への変換もできないと思いますし、
また、そもそも、std::istream&からNLComponent*への変換もできないと思いますし、
επιστημηさん、Banさんお世話にお世話になります
>>επιστημηさん
シリアライズの方法をよく、学習します、良いサイトのご紹介ありがとうです
Banさん
>>本当に提示範囲でコードを書いているならば、根本的に理解が間違っていませんか。
std::istream&からNLComponent*への変換もできないのも重々承知なのですが
書籍のコメントを読んだら、何か特別な方法でもあるのかと、混乱しているしだいです。
また、readComponent()関数の部分は、間違えやすいという具合に、本文にも載っている
のですが
その肝心な部分のコードが、欠落しているしだいです、私の理解力の無さもありますが、
翻訳の意味が分かりません、やっぱりできないですよね、こんな実装は。
単純に考えて、std::fstreamクラスを使うことにします。
尚、このコメントは、書籍からの引用です。
// strから次のNLComponentのデータを読み込み、
// コンポーネントを生成してインスタンスを返す
// readComponentから返されたポインタを
// コンポーネントリストの最期に追加する
// push_backは、リストの終りに挿入する
// リストメンバ関数である
ファイルを開いて渡すのは、上位(そのクラスを、使う方)の仕事です。
そこに書くべきではありません。
そこでは、単に引数のistreamから(ifstreamに対してするように)読み出すことです。
コメントでも、strから読み出す、と書かれてます。
その型はistreamであり、そこでifstreamを意識しては駄目です。汎用的になりません。
有名な書籍なので内容は知ってます(古い版も持ってます)が、
本のいっていることは問題がなく、そこでファイルを開こうというのが間違いです。
# 概念の勉強ならやってみるのが一番ですが、シリアライズだけならboostって手も…。
> 単純に考えて、std::fstreamクラスを使うことにします。
それは根本的に解決しないと言うか、理解できてないのだと思いますが。
そもそも、「NLComponentの使い方」って想定できてますか?
もしもこのクラスでC:\\foo.txtから読み出したいのなら、
こんな感じのコード(イメージ)になりますが、これは分かりますか。
std::ifstream ifs(C:\\foo.txt, std::ios::in);
if( ! ifs || ! ifs.is_open) throw std::runtime_error(file open error);
NewsLetter newLetter(ifs);
newLetterの中では、fstreamなど使わないからこそ、こんなこともできるわけです。
NewsLetter newLetter(std::cin);
# よそ(リアル)に気を取られて間違い多すぎ…orz
> そもそも、「NLComponentの使い方」って想定できてますか。
そもそも、「NewsLetterの使い方」って想定できてますか。
> if( ! ifs || ! ifs.is_open) throw std::runtime_error(file open error);
> if( ! ifs || ! ifs.is_open()) throw std::runtime_error(file open error);
what effect sweet, the dead leaves plants beechnuts a scientist.
>>Banさん、どうもお世話になります
>>そもそも、「NewsLetterの使い方」って想定できてますか。
NewsLetter型の何かのオブジェクトを返さねばならないと思うのですが、
使い方が想像できません。
間違えました
返すのはNLComponent*型のオブジェクトです。
コンパイルもしてませんし、実際にはこんな処理ではあまり意味もないですが、
行を読んでtext"graphのいずれに応じてコンポーネントを作るとしたら、
たとえばこんな風になると思うわけですが。
NLComponent* NewsLetter::readComponent( istream& str )
{
std::string s;
if(!std::getline(str, s)) throw std::runtime_error(read error);
if(s == text)
{
static TextBlock text;
return text.clone();
}
else if(s == graph)
{
static GraphBlock graph;
return graph.clone();
}
else throw std::runtime_error(unknown block);
}
いやそもそもの前提として継承派生の話がわかってないだけとみた
ifstream f;
istringstream s;
readcomponent(istream& i); // の3つがあるとき
readcomponent(f); も readcomponent(s); も、どっちも正しく処理される
i.e.; istream& i=f; も istream& i=s; も原理的に正しいコード
class base { ... };
class derived1 : public base { ... };
class derived2 : public base { ... }; // があるとき
base* p=new derived1; も base* p=new derived2; も正しい。
だから readcomponent の中で ifstream を使うのは根本が間違ってるということ
Banさん、tetrapodさんありがとうございます
>>Banさんへ
>>コンパイルもしてませんし、実際にはこんな処理ではあまり意味もないですが
著者が、どのよう意図でを持って書いたか、どう動作するのか知りたかったので、
十分教えて頂いた、意義はありました。それから、
Banさんが書いてくださった、
NLComponent* NewsLetter::readComponent( istream& str )この関数の部分で、
static TextBlock text;
static GraphBlock graph;
この記述がありますが、ここでの、この二つのクラス名に、staticを付加した理由を、教
えて頂けないでしょうか。
tetrapodさんへ
>>いやそもそもの前提として継承派生の話がわかってないだけとみた
頭では、public継承の場合、is a の関係が成り立つことは、理解できてた
つもりだったのですが
istream とifstreamの関係で混乱してしまいました。
> staticをつけた理由
cloneだったから、ですね。