- ベストアンサー
クラス設計について
C++で通信データのクラス設計をしております。 実際の通信手順ではなく、通信データそのもののクラスの作り方で悩んでおります。 256byteのデータの先頭に識別IDがあり、データを受け取ったらそのID用のデータ表示をするようなことをしたいのですが、どのようにクラスを作ればいいのかが分かりません。 今考えているのは、ベースクラスを作り、そのベースクラスを継承した各ID用の子クラスを作っているのですが、もっといいやり方、いい例などがあれば教えてください。 初めてのクラス設計で悩みまくっています。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
> 1.DataBaseのコンストラクタでlong id_にデータを代入しているところですが、{0,0,0,1}の並びだと16777216という値で格納されてしまい、create()のswitch文のgetId()id_を取り > 出すと1に該当しなくなってしまいます。 ああ、すんません。パソコン(Intel系のプロセッサ)を使ってるんですね。 Intel系のプロセッサは、バイトの並びが反転するんです。long で 1 ならば、 {0,0,0,1} ではなく {1,0,0,0} としてみてください。 Webサイトを検索するのなら「エンディアン」とか「バイトオーダ」をキーワードに して検索をしてみてください。 > 2.DataTypeA,Bのコンストラクタでid_をのぞいたデータを代入するわけですが、 > たくさんの異なった型が並んでいる場合、ポインタを使わずに簡単に代入できるテクニックなどってありますでしょうか? テクニックというほどではないですが、それぞれの識別ID毎にデータの構造を記述した 構造体を宣言して、それを利用すると、ソースの読みやすさは向上します。 先の回答の DataTypeA のところだけをちょっと書き直してみます。 //------------------------------------------------------------------------ // TYPE 1 // class DataTypeA : public DataBase { public: DataTypeA(char*); ~DataTypeA() {} void print() const; private: short s1_, s2_; struct DataTypeA_info { // このクラスでしか使わないんだから private で十分 short s1; short s2; }; }; DataTypeA::DataTypeA(char* buf) : DataBase(buf) { DataTypeA_info* p = (DataTypeA_info*)getBuf(); s1_ = p->s1; s2_ = p->s2; } どうせ、構造体を記述するのであれば、その構造体をそのままメンバに持つという 選択もありますね。 //------------------------------------------------------------------------ // TYPE 1 // class DataTypeA : public DataBase { public: DataTypeA(char*); ~DataTypeA() {} void print() const; private: struct DataTypeA_info { // このクラスでしか使わないんだから private で十分 short s1; short s2; } info_; }; DataTypeA::DataTypeA(char* buf) : DataBase(buf) { info_ = *(DataTypeA_info*)getBuf(); }
その他の回答 (1)
- a-kuma
- ベストアンサー率50% (1122/2211)
> 今考えているのは、ベースクラスを作り、そのベースクラスを継承した各ID用の子クラスを作っているのですが、 基本的には、その方向で良いんじゃないでしょうか。識別IDによる伝文の種類や その扱い方が変わることを考えて。 ちょっと長くなりますが、簡単に書いてみました。あくまでも、実装の仕方の ひとつとして参考にして下さい。ポイントが幾つか。 ・識別IDが伝文のどこにあるかは基底クラスで実装し、派生クラスには隠蔽 ・データ部(識別ID以降)の扱いは、各派生クラスにお任せ ・識別IDによって、どの実装クラスが生成されるかを DataFactory クラスで実装し、利用者には隠蔽 ・識別IDが想定外だと、例外を送出 ひとつのコードに一気に書いてますが、実際には、各クラス毎にヘッダファイルと ソースファイルを作ります。 もし、扱う識別IDが増えた場合には、それを扱う派生クラスのヘッダファイルと ソースファイルを作り、DataFactory の派生クラスを new する行を増やすだけ で済みますね。 あ、ソースは、そのまま Cut & Paste できるように、漢字の空白を使って いませんので、ブラウザでは、ちょっと読み難いですね。 #include <iostream.h> //------------------------------------------------------------------------ // Data Base (にしても、クラス名が良くないね (^^; // class DataBase { public: DataBase() : buf_(NULL) {} DataBase(char* buf) { buf_ = buf; id_ = *(long*)buf_; } virtual ~DataBase() {} virtual void print() const = 0; long getId() const { return id_; } protected: char* getBuf() const { return buf_ + sizeof(long); } private: char* buf_; long id_; }; //------------------------------------------------------------------------ // TYPE 1 // class DataTypeA : public DataBase { public: DataTypeA(char*); ~DataTypeA() {} void print() const; private: short s1_, s2_; }; DataTypeA::DataTypeA(char* buf) : DataBase(buf) { char* p = getBuf(); s1_ = *(short*)p; p += sizeof(short); s2_ = *(short*)p; } void DataTypeA::print() const { cout << "TYPE A : id = " << getId() << ", " << "s = " << s1_ << " " << s2_ << endl; } //------------------------------------------------------------------------ // TYPE 2 // class DataTypeB : public DataBase { public: DataTypeB(char*); ~DataTypeB() {} void print() const; private: long i1_, i2_; }; DataTypeB::DataTypeB(char* buf) : DataBase(buf) { char* p = getBuf(); i1_ = *(long*)p; p += sizeof(long); i2_ = *(long*)p; } void DataTypeB::print() const { cout << "TYPE B : id = " << getId() << ", " << "l = " << i1_ << " " << i2_ << endl; } //------------------------------------------------------------------------ // Factory // class DataFactory { public: DataFactory() {} ~DataFactory() {} DataBase* create(char*) const; long getId(char* buf) const { return *(long*)buf; } }; class BadIdException { public: BadIdException(long id) : id_(id) {} private: long id_; friend ostream& operator<< (ostream&, BadIdException&); }; ostream& operator<< (ostream& o, BadIdException& ex) { o << "ID = " << ex.id_; return o; } DataBase* DataFactory::create(char* buf) const { DataBase* d; switch (getId(buf)) { case 1: d = new DataTypeA(buf); break; case 2: d = new DataTypeB(buf); break; default: throw BadIdException(getId(buf)); } return d; } //------------------------------------------------------------------------ // test code // int main(void) { char buf1[256] = { 0, 0, 0, 1, 0, 2, 0, 3}; char buf2[256] = { 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 19}; char buf3[256] = { 0, 0, 0, 3}; DataFactory factory; DataBase* data; try { data = factory.create(buf1); data->print(); data = factory.create(buf2); data->print(); data = factory.create(buf3); data->print(); } catch (BadIdException& ex) { cout << "Exception ! (bad id). ex : " << ex << endl; } return 0; }
お礼
ありがとうございました。 C++をはじめてまだ4ヶ月なので解読に時間がかかり、丸々一日考えさせてもらいました。 なるほど、このような感じで作ればよいのですね。すごく勉強になりました。 しかも、ものすごく丁寧にありがとうございます。助かりました。 二つほど教えてほしいです。 1.DataBaseのコンストラクタでlong id_にデータを代入しているところですが、{0,0,0,1}の並びだと16777216という値で格納されてしまい、create()のswitch文のgetId()id_を取り出すと1に該当しなくなってしまいます。 {0,0,0,1}のデータの並びでlongに1を代入するにはどうすればよいのでしょうか? あちこちのWebサイトを探してみましたが説明が載っていません。 開発環境はWindows95なのですがなにか関係があるのでしょうか? 2.DataTypeA,Bのコンストラクタでid_をのぞいたデータを代入するわけですが、 たくさんの異なった型が並んでいる場合、ポインタを使わずに簡単に代入できるテクニックなどってありますでしょうか? すいませんがご教授願います。
お礼
a-kumaさん、非常にありがとうございました。 自分ひとりでやっていて、なんとかプログラムはかけるものの、果たしてその書き方、考え方が合っているかなんて分からなくて非常に自分に自信がありませんでした。 このように、丁寧に専門家の人に見ていただくと、自分に自信が少し湧きます。 ありがとうございました。