ページ 11

C++での子クラスオブジェクトのインスタンス生成方法について

Posted: 2017年5月11日(木) 17:56
by ノット
現在、親クラスAを型に指定しているshared_ptr型のvector配列に親クラスAの子クラスのインスタンスを格納するという関数の作成を目指しています。
関数の具体的な処理ですが、string型の第一引数type、shared_ptr<A>の二次引数objをそれぞれ持ち、
String型の実引数をtypeにて仮引数として呼び出し、if文にてクラスAに格納する子クラスB~Eを選択するというものです。

現在、以下のコード形式にて実装しようとしました。処理内容は例です。

コード:


class Content{
public:
      Content(int _num) : num(_num){
      };

      int num;
}

class A{
public:
     A();
     ~A();
     virtual void Init(Content c){
     };
}

class B : public A{
public:
     B();
     ~B();
     void Init(Content c){
          content = c;
     };
private:
     Content content;
}

//以下、クラスAの子クラスC~Eまで宣言
//処理内容は子クラスBのものと同じ

int main(){
      std::vector<std::shared_ptr<A>> array;
      std::string _type = <別関数から検索用文字列を取得>;
      //略
     for(int i = 0;i < 4;i++){
          std::shared_ptr<A> temp;
          SetInstance(_type, temp);
          array.push_back(temp);
          array.back()->Init(PanelContent(i));
     }
}

//文字列によって参照渡しする子クラスを選択する
void SetInstance(std::string type, std::shared_ptr<A>& obj){
    if(type == "TYPE_B"){
        obj = std::make_shared<B>(B());
    }else if(type == "TYPE_C"){
        obj = std::make_shared<C>(C());
    }
    else if(type == "TYPE_D"){
        obj = std::make_shared<D>(D());
    }else if(type == "TYPE_E"){
        obj = std::make_shared<E>(E());
    }
}



上記のコードの内容で実行したところ、配列arrayに格納された変数はすべてが親クラスAの型を持つものになってしまい、関数Init()で渡された変数も格納されずじまいとなってしまいました。

上記のコードの方法で間違いの点や、vector配列に同じ親クラスの別の子クラスのインスタンスを代入する処理の実装方法について教えて頂ければ幸いです。

使用環境はWindows8.1、VisualStudio2015です。また、作品制作にDXライブラリを使用していますが、上記の処理ではまだ関係しません。

Re: C++での子クラスオブジェクトのインスタンス生成方法について

Posted: 2017年5月11日(木) 20:33
by ノット
上記の40行目に誤りがありました。
正しくは以下の通りです。大変失礼いたしました。

コード:

         array.back()->Init(Content(i));

Re: C++での子クラスオブジェクトのインスタンス生成方法について

Posted: 2017年5月14日(日) 02:54
by かずま
ノット さんが書きました:現在、親クラスAを型に指定しているshared_ptr型のvector配列に親クラスAの子クラスのインスタンスを格納するという関数の作成を目指しています。
ポインタ型の配列に子クラスのインスタンスを格納することはできません。

その配列の要素は、基底クラス(親クラス)のインスタンスへの
ポインタ変数だから、派生クラス(子クラス)のインスタンスへの
ポインタの値を格納することはできます。
ノット さんが書きました:上記のコードの内容で実行したところ、配列arrayに格納された変数はすべてが親クラスAの型を持つものになってしまい、関数Init()で渡された変数も格納されずじまいとなってしまいました。
配列array の要素は、shared_ptr<A> なので、
A型ではなく、A型へのポインタです。
Init() で渡された値が content.num に格納されていないことを
どうやって確認しましたか?

試しに次のようなコードを書いてみましたが、何もおかしいことはありません。

コード:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class Content {
public:
    Content(int _num = 0) : num(_num) { };
    int num;
};
 
class A {
public:
    virtual void Init(Content c) { };
    virtual void Print() { std::cout << "A::Print()\n"; }
};
 
class B : public A {
public:
    void Init(Content c) { content = c; }
    void Print() { std::cout << "B: content = " << content.num << "\n"; }
private:
    Content content;
};
 
class C : public A {
public:
    void Init(Content c) { content = c; }
    void Print() { std::cout << "C: content = " << content.num << "\n"; }
private:
    Content content;
};
 
std::string GetType()
{
    std::string type;
    std::cout << "TYPE_B or TYPE_C: ";
    std::cin >> type;
    return type;
}

void SetInstance(std::string type, std::shared_ptr<A>& obj);
 
int main()
{
    std::vector<std::shared_ptr<A>> array;
    std::string _type = GetType();
    for (int i = 0; i < 4; i++) {
        std::shared_ptr<A> temp;
        SetInstance(_type, temp);
        array.push_back(temp);
        array.back()->Init(Content(i));
    }
    for (int i = 0; i < 4; i++)
        array[i]->Print();
}
 
void SetInstance(std::string type, std::shared_ptr<A>& obj)
{
    if (type == "TYPE_B")
        obj = std::make_shared<B>(B());
    else if(type == "TYPE_C")
        obj = std::make_shared<C>(C());
}

Re: C++での子クラスオブジェクトのインスタンス生成方法について

Posted: 2017年5月14日(日) 18:16
by ノット
>かずま さん
ご返信ありがとうございます。
かずま さんが書きました:Init() で渡された値が content.num に格納されていないことを
どうやって確認しましたか?
先のコードの確認方法ですが、以下のコードにて配列に
それぞれ格納された変数の型をファイルに出力するという方法を取りました。

コード:

    std::ofstream writing_file;
    writing_file.open("test.csv", std::ios::out);

    for(auto a : array){
        const type_info& id = typeid(A);
        writing_file << id.name() << std::endl;
    }

これで配列の各要素ごとに派生クラスの型の変数が格納されると考えていたのですが、
例:
class std::shared_ptr<class B>
class std::shared_ptr<class B>
class std::shared_ptr<class C>
...

実際にはすべて

class std::shared_ptr<class A>

のように基底クラスの型の変数が格納されるという出力でした。
以上の結果と本来考えていた動作ができなかったことから、「配列に正しく格納できていない」と判断したのですが、
私の想定自体が間違っているのでしょうか?

ご返信いただければ幸いです。

Re: C++での子クラスオブジェクトのインスタンス生成方法について

Posted: 2017年5月14日(日) 20:26
by かずま
ノット さんが書きました:

コード:

    std::ofstream writing_file;
    writing_file.open("test.csv", std::ios::out);

    for(auto a : array){
        const type_info& id = typeid(A);
        writing_file << id.name() << std::endl;
    }
typeid(A) は、typeid(a) の間違いですね。
typeid(A) だと、id.name() は class A になるはずです。
typeid(a) だと、id.name() が class std::shared_ptr<class A> になるのは当然。
std::vector<std::shared_ptr<A>> array; と宣言しているのですから、
array の要素である a の型は std::shared_ptr<A> です。
ノット さんが書きました:>これで配列の各要素ごとに派生クラスの型の変数が格納されると考えていたのですが、
配列の各要素ごとに「派生クラスの型の変数へのポインタの値」が格納されます。
でも格納先の配列要素の型は、あくまでも「基底クラスの型の変数へのポインタ」です。

基底クラスへのポインタでありながら、
そのポインタの指すものは「派生クラスのインスタンス」です。
だから、Init() で初期化した content.num を持っています。
ただ、A へのポインタで content.num にアクセスすることはできません。
そこで、私の書いたコードのように、virtual void Print(); が必要なのです。

Re: C++での子クラスオブジェクトのインスタンス生成方法について

Posted: 2017年5月15日(月) 01:22
by ノット
>かずま さん
ご返信ありがとうございます。
かずま さんが書きました: std::vector<std::shared_ptr<A>> array; と宣言しているのですから、
array の要素である a の型は std::shared_ptr<A> です。

配列の各要素ごとに「派生クラスの型の変数へのポインタの値」が格納されます。
でも格納先の配列要素の型は、あくまでも「基底クラスの型の変数へのポインタ」です。

基底クラスへのポインタでありながら、
そのポインタの指すものは「派生クラスのインスタンス」です。
大変失礼いたしました。変数の型にはあくまで基底クラスとして入れられ、
中身のポインタの示す先として派生クラスのインスタンスが呼び出せるというわけですね。

また、かずまさんのコードをもとに私のコード内に文字表示を行う関数を追加して実行したところ、
各配列に変数がきちんと入れられていることと、また別に問題があったことが分かりました。

目的の状況には至っていませんが、表題の問題が分かったため一旦このスレは解決とさせて頂きます。

また今後、私の稚拙な質問に応対していただければ幸いです。

この度は本当にありがとうございました。