基底クラスで子クラス参照

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kazuoni

基底クラスで子クラス参照

#1

投稿記事 by kazuoni » 16年前

お邪魔します。

今回は基底クラスで
子クラス参照ができるのかどうなのか教えていただきたいです。

例を挙げてみます
基底(親)クラス・・・mammal(哺にゅう類)
派生クラス・・・human,dog,house...
哺にゅう類は自分の親と同種から生まれ、同種の子を持ちます。
この時、mammalクラスに年齢、性別などの情報の他に
親、子が誰なのかを持たせたいです。(親は父母一人ずつ、子は複数)

自分が考えたのは
例えば、humanインスタンス生成時に
両親が誰なのかを参照できるようにし、
子供が生まれたときに子供が誰か参照できるようにする機能
を基底クラスmammalで管理したいのです。
(親がいる、子が生まれるのはmammal共通の為)
基底クラスで、子クラスを
テンプレートみたいにうまく扱えないでしょうか?
そもそもこの管理は不可能でしょうか?
(自分的には、直観的に分かりやすかったのですが・・・。)

また、みなさんなら、親と子の関係を(基底)クラスで管理するとすれば、
どのように実装しますか?(どのように実装できますか?)

よろしくお願いします。

dic

Re:基底クラスで子クラス参照

#2

投稿記事 by dic » 16年前

意図がつかめてなかったらすいません
#include	<iostream>
using namespace std;

class	Animal
{
	char	name[80];
public:
	void	Show();
	void	SetName( char *c );
};

class	Human	:	public	Animal
{
	Animal	*animal;
public:
	void	SetParent( Animal *a );
	void	CallParent();
};

void	Animal::SetName( char *c )
{
	strcpy( name, c );
}
void	Human::SetParent( Animal *a )
{
	animal = a;
}
void	Human::CallParent()
{
	cout << "親は";
	animal->Show();
}

void	Animal::Show()
{
	cout << name << endl;
}


void	main()
{
	Animal	*a = new Animal;
	Human	*h = new Human;

	a->SetName( "イルカ" );
	h->SetName( "たろべぇ" );

	a->Show();
	h->Show();

	h->SetParent( a );
	h->CallParent();

	delete a;
	delete h;
}
こんなのではどうでしょうか?

kazuoni

Re:基底クラスで子クラス参照

#3

投稿記事 by kazuoni » 16年前

dicさん、ご回答有難う御座います。

折角解答してくださったのですが、
これでなら確かに実装できるのですが、
親、子供を持つのは哺にゅう類共通ですので、
これを基底クラスで管理したいのです。

っと書きながら自分でつっこみます。。
基底クラスで親、子供のクラスをテンプレートみたいに持てたとしても
基底クラスでは、その内容が分からないので、どうも扱えそうに無いですね。

...やはり、
人間は人間の親と人間の子を持つ
犬は犬の親と、犬の子を持つ
...

としたほうが自然ですかね?^^;

sizuma

Re:基底クラスで子クラス参照

#4

投稿記事 by sizuma » 16年前

僕は基底クラスに利用するメソッドを定義しておく方がいいと思います。

僕は勉強不足でOOPのデザインパターンを理解できてるわけではないんですが。
のなかで、templateMethoodパターンっていうのがあるんですが(検索すればヒットすると思います


この場合だと、哺にゅう類っていう特に処理を持たないクラスを窓口にして、実際に作った派生クラスのインスタンスにアクセスするように実装するのが拡張性が高くなると思います。
class Animal{
      virtual void showParent(){
      }
}


class Human : Animal{
      override void showParent(){
            Console.WriteLine("人間の子と親を持つ");
   }
}


//アクセスするのはanimal型の変数から

     Animal human = new Human(); 
     human.showParent();
C++勉強中なんで、C#です・・・

GPGA

Re:基底クラスで子クラス参照

#5

投稿記事 by GPGA » 16年前

>人間は人間の親と人間の子を持つ 
>犬は犬の親と、犬の子を持つ 

これでいいんじゃないですかね。

class Human {
	Human* father_;	// 父
	Human* mother_;	// 母
	std::vector<Human*> child_; // 子

public :
	// 両親のセット
	void SetParents(Human* father, Human* mother) {
		father_ = father;
		mother_ = mother;
	}

	// 子のセット
	void SetChild(Human* child) {
		child_.push_back(child);
	}
};
 

kazuoni

Re:基底クラスで子クラス参照

#6

投稿記事 by kazuoni » 16年前

sizumaさん、GPGAさん、ご回答有難う御座います。

自分はGPGAさんとほぼ(っというか、humanは全く同じ)様に実装しておりました。
ただ、これだと、dogクラスが新たに出てきても、またfather,mother,children
をメンバに持たせなければいけないので、
なんとか基底クラスで上手くまとめられないかと試行錯誤していたのです。。

dic

Re:基底クラスで子クラス参照

#7

投稿記事 by dic » 16年前

Decorator パターンではどうでしょうかね?
#include	<iostream>
using namespace std;

class	Animal
{
	char	name[80];
public:
	void	Show();
	void	SetName( char *c );
};

class	Decorator	:	public	Animal
{
protected:
	Animal	*_father;
	Animal	*_mother;
public:
	void	SetParent( Animal *fathre, Animal *mother );
	void	CallParent();
};

class	Human	:	public	Decorator
{
public:
	//	Human 専用メンバ関数
};

class	Dog		:	public	Decorator
{
public:
	//	Dog 専用メンバ関数
};


void	Decorator::SetParent( Animal *father, Animal *mother )
{
	_father = father;
	_mother = mother;
}
void	Decorator::CallParent()
{
	cout << "父親は";
	_father->Show();

	cout << "母親は";
	_mother->Show();
}

void	Animal::SetName( char *c )
{
	strcpy( name, c );
}

void	Animal::Show()
{
	cout << name << endl;
}


void	main()
{
	Animal	*f = new Animal;
	Animal	*m = new Animal;
	Human	*h = new Human;

	f->SetName( "たろべぇの父" );
	m->SetName( "たろべぇの母" );
	h->SetName( "たろべぇ" );

	f->Show();
	m->Show();
	h->Show();

	h->SetParent( f, m );
	h->CallParent();

	delete f;
	delete m;
	delete h;
}

sizuma

Re:基底クラスで子クラス参照

#8

投稿記事 by sizuma » 16年前

僕だったらこうですかねー
(大部分をGPGAさんからコピペしました。vectorがどう管理してるかとか分からずのコメントなんでなんか勘違いしてたらごめんなさい)
class Animal {

	Animal* father_;	// 父

	Animal* mother_;	// 母

	std::vector<Animal*> child_; // 子



public :

	// セット

	Animal(Animal* father, Animal* mother,Animal* child) {

		father_ = father;

		mother_ = mother;

                child_.push_back(child)

	}
};

class Human : Animal{
       Human(Animal* father, Animal* mother,Animal* child){
            base();
       }
};
というのはダメでしょうか?
フィールドが全部同じならこれで大丈夫かな?
ていうかC++わからなすぎるんでちょっと勉強してきますーー

dic

Re:基底クラスで子クラス参照

#9

投稿記事 by dic » 16年前

基底クラスで管理でき なおかつ拡張できそうでしたら
#include	<iostream>
using namespace std;


class	Human;
class	Dog;

class	Animal
{
	Human	*_h;
	Dog		*_d;
public:
	Animal();
	Animal( Human *h );
	Animal( Dog *d );
	void	Show();
};
class	Human
{
public:
	Human();
	void	Show();
};
class	Dog
{
public:
	Dog();
	void	Show();
};

Animal::Animal( Human *h ){
	_h = h;
	_d = 0;
}
Animal::Animal( Dog *d ){
	_h = 0;
	_d = d;
}
Animal::Animal(){ _h = 0; _d = 0; }
Human::Human(){}
Dog::Dog(){}


void	Animal::Show() {
	cout << "Animal" << endl;
	if( _h )	_h->Show();
	if( _d )	_d->Show();
}
void	Human::Show()  { cout << "オラァ人間だぁ!"  << endl; }
void	Dog::Show()  { cout << "ワンワン"  << endl; }


void	main()
{
	Human	*h = new	Human;

	Animal	*a = new Animal(h);

	a->Show();


	delete h;
	delete a;

	Dog		*d = new	Dog;
	a = new Animal(d);

	a->Show();
	
	delete d;
	delete a;
}
なんていかがでしょう?

kazuoni

Re:基底クラスで子クラス参照

#10

投稿記事 by kazuoni » 16年前

dicさん、sizumaさん、ご回答有難う御座います。

sizumaさんのコードの
親クラスを基底クラスメンバに持たせれば
自分の本当のやりたいことが上手来そうな気がしてきました。


>dicさん
本当に何度もソースを組んでいただいて有難う御座います。
今回は基底クラスからの派生クラスを用いることが自分の中で前堤となってました。。
説明不足ですみませんでした。
挙げていただいたコードは必ず一度自分の手で組み、実行、検討させていただきます!
本当に有難う御座います。


今、ちょっと出来ないですが、
帰ったら皆さんのコードを参考に組んでみたいと思います。

kazuoni

Re:基底クラスで子クラス参照

#11

投稿記事 by kazuoni » 16年前

組む前に早速疑問が・・・。

親・子にアクセスするときは、sizumaさんのコードでは、
常に基底クラスでアクセスするわけで、
年齢とか等、情報は得ることはできますが、
親・子のデータを子クラスというクラスからは操作できないですよね?

例えば、
第一世代のインスタンスを生成し、第二世代が誕生したとします。
第二世代は常に基底クラスとしてアクセスするので、人間としてアクセスするのではなく、
哺にゅう類でアクセスすることになります。
すると、人間クラスのみで操作したいものは、
第二世代以降使用できないということになる気がするのですが・・・
どうでしょうか?

sizuma

Re:基底クラスで子クラス参照

#12

投稿記事 by sizuma » 16年前

もしかしたら、僕が質問の意味をちゃんと理解できていないかもしれないんですが(汗
派生クラス(人間)で新たな機能をもつメソッドを定義した場合、利用できなくなる、ということでしょうか?

それならば、あまり美しくない気がしますが(笑)キャストすれば、アクセスできるようになります。

例えば
Animal human = new human();
として宣言して、humanに新しいメソッドcodeWrite()を定義したら、このままではアクセスできませんが、

Human super_human = (Human)human;
//基底クラスの変数(もちろんインスタンスは派生クラス) ⇔ 派生クラスの変数

として、Human型にキャストすることができます。
逆に親クラス型の変数にはキャストが必要ないんですけどね。

でも直感的に分かりやすく書けそうにないんで、クラス設計を他のアプローチに変えたほうがいいかもしれませんね。


と、ここまで書いて、C++だったらポインタとかでなんか違うのかな?とか思ってきました・・・

Justy

Re:基底クラスで子クラス参照

#13

投稿記事 by Justy » 16年前

「犬や猫、人間といったクラス全てをインスタンスの保持を基底クラスの
ポインタ(あるいは参照)で行うので「哺ニュウ類クラス」という基底でまとめる必要があり、
親子関係を結ぶメソッドも同じ基底クラスで実装しておきたい」

のか
「ただ単に共通するメソッドなので、とりあえず基底でまとめたい」

のどちらでしょう?

 後者であれば、「哺ニュウ類クラス」に親子関係を入れる必要はないように思いますが。

GPGA

Re:基底クラスで子クラス参照

#14

投稿記事 by GPGA » 16年前

以下のやり方でできないことも無い。
template <class T>
class Mammal {
	T* father_;
	T* mother_;
	std::vector<T*> child_;
};


// パターン1
// 人間クラス
class Human : public Mammal<Human> {

};

// パターン2
// 犬クラス
class Dog {

};

int main()
{
	// パターン1
	Human human;

	// パターン2
	Mammal<Dog> dog;
}
 

Justy

Re:基底クラスで子クラス参照

#15

投稿記事 by Justy » 16年前


>後者であれば、「哺ニュウ類クラス」に親子関係を入れる必要はないように思いますが。

 例えば、こんなかんじだと、Relationshipクラスは他にも使い回せます。

[color=#d0d0ff" face="monospace]
#include <memory>
#include <list>
#include <algorithm>

// 哺ニュウ類
class Mammal
{
public:
enum Gender { Gender_Male, Gender_Female };

Mammal(Gender gender) : m_Gender(gender) {}
virtual ~Mammal() {}

Gender GetGender() const { return m_Gender; }

private:

Mammal::Gender m_Gender;
};

// 関係性
template <class T>
class Relationship
{
public:
typedef T Type;
Relationship() : m_Father(), m_Mother() {}
Relationship(Type *father, Type *mother) : m_Father(father), m_Mother(mother) {}

virtual ~Relationship()
{
// 適切にリストから破棄する
}

Type * CreateChild(Type *parent, Mammal::Gender gender)
{
Type * this_ = static_cast<Type*>(this);
if(!parent || this_->GetGender() == parent->GetGender()) return 0;

Type *child;
if(parent->GetGender() == Mammal::Gender_Male)
child = new Type(gender, parent, static_cast<Type*>(this));
else
child = new Type(gender, this_, parent);
m_Childrens.push_back(child);
parent->m_Childrens.push_back(child);
return child;
}

// 親子関係を取得するメソッドを追加しておく

private:

typedef std::list<Type*> ListType;

Type* m_Father;
Type* m_Mother;
std::list<Type*> m_Childrens;
};

// 人
class Human
: public Mammal, public Relationship<Human>
{
public:
typedef Relationship<Human> RelationshipType;
Human(Gender gender) : Mammal(gender) {}
Human(Gender gender, Human *father, Human *mother)
: Mammal(gender), RelationshipType( father, mother) {}
};

// 男女が
Human humanM1(Human::Gender_Male), humanF1(Human::Gender_Female);

// 3人の子を産む
std::auto_ptr<Human> humanChild1a(humanM1.CreateChild(&humanF1, Human::Gender_Male));
std::auto_ptr<Human> humanChild1b(humanM1.CreateChild(&humanF1, Human::Gender_Female));
std::auto_ptr<Human> humanChild1c(humanM1.CreateChild(&humanF1, Human::Gender_Female));

// 長女が二人の産む
Human humanChildM2(Human::Gender_Male);
std::auto_ptr<Human> humanChild2a(humanChild1b->CreateChild(&humanChildM2, Human::Gender_Male));
std::auto_ptr<Human> humanChild2b(humanChild1b->CreateChild(&humanChildM2, Human::Gender_Female));

// 長女の長男が叔母と!
std::auto_ptr<Human> humanChild3a(humanChild2a->CreateChild(humanChild1c.get(), Human::Gender_Female));

// 長女の長男が同性と!(生成されない)
Human humanChildM3(Human::Gender_Male);
std::auto_ptr<Human> humanChild3b(humanChild2a->CreateChild(&humanChildM3, Human::Gender_Female));
[/color]


※ std::auto_ptrを使って居ますが、普通に Human*でもかまいません。

kazuoni

Re:基底クラスで子クラス参照

#16

投稿記事 by kazuoni » 16年前

sizumaさん、Justyさん、GPGAさんご回答ありがとうございます。

>sizumaさん
もしキャストが使えるとしても、なんだか気持ちがよくはないですね^^;
拡張している感じが。。

またもや、GPGAさんの回答が自分のコードが自分の書いていたのとドンピシャ
だったんですが、
Humanのコンストラクタで、Mammalのコンストラクタを呼ぼうとすると、
どうしても未解決の外部シンボルとでてしまうので、
これはできないんじゃ。。っと思って、今回のスレを立てるに至りました。
こんな感じにしています。
Mammalコンストラクタ
template <class Temp>
Mammal<Temp>::Mammal(Temp* Father , Temp* Mother ,int Gene , bool 性別 , int Born_max , int Age , int Child_num , bool Ismarry)
:	father(Father) ,
	mother(Mother) ,
       ......
{}

Humanコンストラクタ
Human::Human(Human* father , Human* mother ,int Gene , bool 性別 , int Age , int Child_num , bool Ismarry)
:	Mammal(father , mother , Gene , 性別 , born_max ,Age , Child_num , Ismarry)
{}
HumanでMammalのコンストラクタを呼ぶ時、Mammal<Human>(...)とか試しましたが、
結局は同じく、未定義エラーが出てしまいます。
・・・できないこともないということは、無理やりすぎますかね?^^;


>Justyさん
恐らく後者かと思います。。
なるほど。親子関係を新たなクラスにし、基底クラスに持たせるのですね。
確かにこちらのほうが、クラスの機能がはっきりしています。
上に挙げた自分のコードは、Mammalですべてをくくってしまってるので、
管理がしづらくなる可能性がある気がしてきました。


#今回NGワードが多くて、何回も投稿できませんでしたがでました・・・。

GPGA

Re:基底クラスで子クラス参照

#17

投稿記事 by GPGA » 16年前

テンプレートを使う場合、メソッドの定義はクラス定義内に書く必要があります。


正しい書き方
-------------------------------------------------------------------------------------------
☆mammal.h
template <class T>
class Mammal {
	T* father_;
	T* mother_;
	std::vector<T*> child_;

protected :
	Mammal(T* father, T* mother) : father_(father), mother_(mother) {}
};
-------------------------------------------------------------------------------------------

間違っている書き方
-------------------------------------------------------------------------------------------
☆mammal.h
template <class T>
class Mammal {
	T* father_;
	T* mother_;
	std::vector<T*> child_;

protected :
	Mammal(T* father, T* mother);
};

☆mammal.cpp
template <class T>
Mammal<T>::Mammal(T* father, T* mother) : father_(father), mother_(mother) {}
-------------------------------------------------------------------------------------------
 

kazuoni

Re:基底クラスで子クラス参照

#18

投稿記事 by kazuoni » 16年前

GPGAさん、ご回答ありがとうございます。
また一つ勉強になりました。
テンプレートはほとんど使ったことがなかったもので。。

いろいろ実装してみて、最終的に解放のところで行き詰まりました。。
解放では、今までの生まれてきた子供たちです。
自分の考えでは、
ある一番最初に生まれた人から再帰的に子供を見ていって、
ある子どもが、子どもを持たないなら、両親の子どものvector
から外していき、解放っと考えたのですが、
自分の組み方では、親も子も、テンプレート型なので、
基底クラスでの解放は不可能になり、結局は、新たな解放クラス(基底はMammal)
を作り、子クラスに持たせる必要性が出てきました。
→生成、破棄を同じクラスにする
→結局はJustyさんの親子クラスですね^^;

これで、基底クラスですべて管理することになんのメリットもなくなりました^^;
おとなしくJustyさんのおっしゃるように、親子関係をクラスにします。

これで自分の作る方針ができました。
ここで、今回の質問は解決とさせていただきます。
ご回答していただいた皆さん、本当にどうもありがとうございました。

もっといろいろ継承等を実際に組んで、経験しないといけないなと実感しました。

本当にありがとうございました。
次回もよろしくお願いします。

閉鎖

“C言語何でも質問掲示板” へ戻る