質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.36%

  • C++

    2409questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C++ 同インスタンス、別クラス

受付中

回答 3

投稿 2017/12/08 17:21

  • 評価
  • クリップ 1
  • VIEW 114

iSnow

score 0

前提・実現したいこと

C++で通信プログラムを作成しています。初心者です(汗
通信プロトコルには複数のフレームがあり、そこに記載する内容はさまざまです。
そこで、それぞれのフレームを別々のクラスにし、各クラスでフレームに記載できる内容をメソッドに持たせようと考えました。
例えば、Aフレームで通信するときは、Aフレームの各メソッドに記載内容を渡し(コマンドやサイズなど)最後にgetPacketなどで生成されたデータを取得する。
といった感じです。
そこで、これらのフレームを統合するクラスをつくりたいです。
これの利点は、
・どのフレームを使おうと統合したクラスをincludeまたは継承すれば使える。
・インスタンス化したものに対し使用できるメソッドが限られるため、エラーが減る
だと思っています。
言葉では分かりづらいと思うのでソースコードで理解していただければ幸いです。

私のやりたいことはC++で可能なのでしょうか?

発生している問題・エラーメッセージ

invalid conversion

該当のソースコード

class A{
 A();
 void method;
}
class B{
 B();
 void method;
}
class All{
 All();
  All* getInstance(type){
   switch(type){
    case A:
      A ai = new A();
      return ai;
    case B:
      B bi = new B();
      return bi;
   }
   return new All();  
}

試したこと

やはり、型が違うと怒られます。
やりたいことは、getInstanceに入力したtypeによってクラスを選択しそのインスタンスを取得することです。こうすれば、各クラスのもつメソッドしか使えないためエラーも減り、使いやすくなると考えています。
よろしくお願いいたします。

補足情報(言語/FW/ツール等のバージョンなど)

より詳細な情報

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+2

やりたいことは、getInstanceに入力したtypeによってクラスを選択しそのインスタンスを取得することです。こうすれば、各クラスのもつメソッドしか使えないためエラーも減り、使いやすくなると考えています。

この考えはよくわかりません。
同じ関数/メソッドから入手したものを、同じように使えないのでは不便ではないですか?

共通するインターフェースを持つ場合

継承を用いることによって、すっきりする可能性があります。

class Base {
public:
    virtual void method(void) = 0;
    virtual ~Base(void) = default;
};

しかし、明確なis-a関係でない場合は、むしろ使いづらくなる恐れもあります。

共通するインターフェースが少ない場合

処理自体は簡単なので、テンプレートで実現できます。
しかし、やはり冒頭に書いたように、あまり便利だとは思えません。

投稿 2017/12/08 17:59

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/08 18:43

    ご回答いただきありがとうございます。
    だいぶ大雑把な例になってしまいますが、人と鳥というクラスを作ったとします。それぞれは共通の生き物なので、生き物クラスを作ります。
    やりたかったことは、
    生き物クラスの中で、人か鳥を選択するメソッドを作成し、対応するインスタンスを返すことです。
    人クラスのインスタンスを受け取ったら、サイズ、重量、国籍などを設定でき、
    鳥クラスのインスタンスを受け取ったら、サイズ、重量、羽の長さなどを設定できるようにします。
    こうしたとき、同じ名前のインスタンスで別々のことができると考えたのですが・・・

    おっしゃられた通り、あまり便利ではないかもしれませんね。
    仮にできたとしても楽になるとすれば、人か鳥を選択した後とくにきにすることなくコーディングできる程度ですし、(includeなど気にせずちらからない)当初考えていたほど便利ではなかったです。

    私は、テンプレートの存在は知っていましたが使ったこともない程度の人間なのでまだまだプログラムに対する認識が甘いですね(汗
    これからも精進します
    ありがとうございました。

    キャンセル

  • 2017/12/08 18:53

    includeがごちゃごちゃしてしまうのはちょっと問題ありなので、前方宣言について調べてみると良いかもしれませんね。

    キャンセル

  • 2017/12/08 19:02

    ありがとうございます。早速調べてみました!
    C++にはこんな機能があったのですね。。。勉強になります。
    テンプレートも含め理解が追いついていませんが、地道に頑張ります

    キャンセル

0

通信プログラムは組んだことがないので、わかりませんが。

( そのため、用語はわからん。 )

場合( ユーザ入力とか, 使用するプロトコルとか )によって生成するクラスを決定するなら、

デザインパターンのFactoryパターン系 ( Factory, FactoryMethod etc. ) を適用すればいいかと。

単純に考えると、

1. 生成するためのインターフェースクラスを定義する
2. 1を継承&実装したクラスを定義する
3. インターフェース型として生成する

でしょうか。

例えば、IProductインターフェースがあり、それを継承して Product1, Product2クラスを生成する。

UML風に書くと

<< IProduct >>
+ func1( void ) : bool

[ Product1 ] -> IProduct
+ CONSTRUCTOR
+ DESUTRUCTOR
...

[ Product2 ] -> IProduct
同上

みたいになります。

で、引数によってProduct1, Product2のどれかを生成し、IProduct型として返す関数を定義する。
( クラスでもいいが。 )

IProduct* CreateInstance( int sw ){
          if( sw == 1 ){
                 return new Product1();
          }else{
                 return new Product2();
          }
}


こうすれば、

IProduct* で引き受けても実体は sw が 1なら Product1, それ以外なら Product2 のになっている。

といっても、まったく関係のないもの ( ファイルの入出力系クラスと メッセージボックスを表示するクラス とか ) だと無理ですが、

ウィンドウ1 と ウィンドウ2 クラスそれぞれ微妙に違うとかなら可能かも。


[追記]

独自のメソッドが呼べないということで(思いつく)解決策を提示しますね。

方法1   : インターフェース側にそのメソッドの概要 ( C言語だとプロトタイプ宣言ってやつ )を記述する
メリット: 簡単に作れる
デメリット: 実装先も必ず実装する羽目になる
方法2: asmさんが提示されたサンプルコード内のようにキャストで行う
メリット: 独自メソッドを使うことができる
デメリット: どのクラスから生成したかわからないと厳しい
※ Javaなら instanceof でしたっけ? あれでどのクラスから生成したかわかるようになっているようですが、C++は私が知る限りでは、instanceofにそうとうするのが無いので、別の解決策が必要?
( Qtっていうライブラリ内にはあるようですが、それをするためだけに使うのはアレなので... )
方法3: 独自メソッドは直接いじらずに、そのクラスがやる ( サンプルあり )
メリット: publicだけを見た場合、インターフェースを実装してそれ以外 (独自のメソッド) は存在しないような感じに見える?
デメリット: 場合によっては不採用

[方法3を用いた場合]

class IProduct{
      public:
              virtual bool calc( void ) = 0;
};

class CProduct1 : public IProduct{
      public:
              CProduct1(){ /* メンバの初期化等 */ }
              // デストラクタもあり
              bool calc(void){ // このメソッド内で独自のメソッドを呼び出す
                   if( privateMethod1() ){
                          return this->privateMethod();
                   }else{
                          return false;
                   }
              }
      protected:
              bool privateMethod1(){ /* 何らかの処理 */ }
              bool privateMethod2(){ /* 何らかの処理 */ }
      private:
               // もちろんメンバ変数も持っている。
};

( 簡単に言えば間接的呼び出しか。 )

方法4: IProduct, CProduct1, CProduct2 とは別に Product系を管理し間接的に使うようなクラスを定義してソイツで間接的に扱う ( 生成時は 新しく追加したクラスのほうで。 )
メリット: やろうと思えば独自メソッドでも使える
デメリット: めんどくさい。クラスが多くなる。場合によっては不採用

[方法4でやった場合]

class IProduct{
      public:
              virtual bool calc( void ) = 0;
};

class CProduct1 : public IProduct{
      public:
              CProduct1(){ /* メンバの初期化等 */ }
              // デストラクタもあり
              bool method1(){ /* 何らかの処理 */ }
              bool method2(){ /* 何らかの処理 */ }
              bool calc( void ){ /* 何らかの処理 */ }
      private:
               // もちろんメンバ変数も持っている。
};

class IProductEx{
      Public:
              virtual bool calcEx( void ) = 0;
              virtual bool clac( void ) = 0;
};

class CProductEx : public IProductEx{
      public:
              CProductEx(){
                         p = new CProduct1(); // 生成 & コンポジション
              }
              // デストラクタで p を破棄


              bool calc( void ){
                   return p->calc();
              }

              bool calcEx( void ){
                   if( p->method1() ){
                       return p->method2();
                   }
              return false;
              }
      private:
              CProduct1* p;
};
方法5: すべてインターフェースに詰め込む
メリット: すべて使える
デメリット: あるクラスでは使わないメソッドでも実装しないといけない。またクラスの肥大化。

投稿 2017/12/08 17:36

編集 2017/12/10 11:46

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/08 18:28

    回答していただきありがとうございます!
    実際に書いてみたところ、回答していただいた通り別のクラスのインスタンスを取得することができました!
    ただ・・・プログラマの皆さんには当たり前と言われてしまうのでしょうがProduct1, Product2で別々のメソッドを持っていた場合参照することができませんでした。
    確かに、考えてみればコンパイラはどれを選択するかわからないためIProductにないメソッドが呼ばれた場合はエラーを返します。
    そこで、IProductに該当のメソッドを仮に追加し、CreateInstanceした後でメソッドを読んだのですが、IProduct内のメソッドが呼ばれてしまいました。
    なので、想定どうりには行かず方法を変えるしかないようです・・・
    せっかくご回答いただいたのに役立てることができず申し訳ありません。

    キャンセル

  • 2017/12/10 11:12

    私は趣味でやっているので、詳しくはわかりませんが、
    確かに私が提示した方法だと独自のメソッドは呼べません。

    (思いつく)解決策を(本体の)解答欄に追記しますね。

    キャンセル

  • 2017/12/11 10:32

    大変詳しく追記していただきありがとうございます!
    instanceを取得するものは私自身検索してみましたがみつからず・・・やはりなかったのですね

    むちゃな質問にもかかわらず多くのアイディアを出していただきありがとうございます!
    ソフトウェアはこの柔軟性が楽しくなりますね。
    半ば諦めかけていましたが是非試させてもらいます!

    キャンセル

0

継承とかインタフェースを駆使する事は可能

class Base{
public:
  virtual vector<byte> getPacket() = 0;
}
class A : public Base {
public:
  vector<byte> getPacket();
}
class B : public Base{
public:
  vector<byte> getPacket();
  void set_hoge(int);
}

Base* pfactory = new B();
((B*)pfactory)->set_hoge(0);

投稿 2017/12/08 20:06

編集 2017/12/08 21:00

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/08 21:10

    生ポインタで扱うのであれば、Baseクラスに仮想デストラクタを持たせた方が例としていいのではないでしょうか。

    キャンセル

  • 2017/12/09 00:50

    継承、インターフェースは分かりますが・・・vector....orz
    勉強して出直してきます
    ご回答いただきありがとうございます

    キャンセル

  • 2017/12/09 09:55

    >仮想デストラクタ
    あぁ、確かにそうでした

    >vector
    生バイト列をポインタとして返すと、長さが分からなくなるので
    可変長配列vectorを返す事を思いついた

    ただ、それだけです

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.36%

関連した質問

同じタグがついた質問を見る

  • C++

    2409questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。