コンポジットパターンについて – プログラミング – Home

コンポジットパターンについて
 
通知
すべてクリア

[解決済] コンポジットパターンについて

固定ページ 1 / 2

たに
 たに
(@たに)
ゲスト
結合: 17年前
投稿: 13
Topic starter  

お世話になります。
OOPの勉強としてデザインパターンを学習してまして、
Javaで書かれたソースをC++に移植して勉強している所
です。
質問内容は、Compositeパターンのソースを変換した所、
再帰的に名称を表示する処理がうまく出来ません。
60行目あたりの//☆の箇所で、<list>から取り出した
オブジェクトのDisplay()をコールしているのですが、
オーバーライドがうまく出来ていないのか、スーパー
クラス(Component)のDisplay()を呼んでいます。

オブジェクトの振る舞いもイマイチ良く分からず、
手探りで動かしながら作業をしているのですが、どうにも
分からずに困っています。どなたかお教え願えませんでしょうか?

以下ソース。
------------------------------------------------
#include stdafx.h
#include <iostream>
#include <string>
#include <list>

using namespace std;

class Component
{
public:

string name;
Component()
{
this->name = ";
}

Component(string name)
{
this->name = name;
}

virtual void add(Component c){};
virtual void remove(Component c){};
virtual void display(){};
};

class Composite : public Component
{
private:
list<Component> children;

public:

Composite(string name) : Component(name){};

void add(Component c)
{
children.push_back(c);
}

void remove(Component c)
{
list<Component>::iterator p;
for (p = children.begin(); p != children.end(); p++)
{
Component q = (Component)*p;
if (c.name == q.name) children.erase(p);
}
}

void display()
{
cout << this->name << endl;

list<Component>::iterator p;
for (p = children.begin(); p != children.end(); p++)
{
Component *c = &(Component)*p;
c->display(); //☆
}
}
};

class Leaf : public Component
{
public:

Leaf(string name):Component(name){}

void add(Component c){}
void remove(Component c){}
void display()
{
cout << name << endl;
}
};

int _tmain(int argc, _TCHAR* argv[])
{

Component *root = new Composite(root);
Component *n01 = new Leaf(01);
Component *n02 = new Leaf(02);
root->add(*n01);
root->add(*n02);

Component *compA = new Composite(compA);
Component *A01 = new Leaf(A1);
Component *A02 = new Leaf(A2);
compA->add(*A01);
compA->add(*A02);
root->add(*compA);

Component *compB = new Composite(compB);
Component *B01 = new Leaf(B1);
Component *B02 = new Leaf(B2);
Component *B03 = new Leaf(B3);

compB->add(*B01);
compB->add(*B02);
compB->add(*B03);
root->add(*compB);

root->display();

delete root;
delete compA;
delete compB;
delete n01;
delete n02;
delete A01;
delete A02;
delete B01;
delete B02;
delete B03;

return 0;
}

<<現状の表示>>
root

<<期待する表示>>
root
01
02
compA
A1
A2
compB
B1
B2
B3

環境は、WinXP+VC.net2005です。宜しくお願いします。


引用未解決
トピックタグ
aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

list<Component>.push_back は、Component のコピーを格納します。
引数に Component の派生クラスを渡すと、派生型の内の Component 部分だけのコピーが
作られて格納されます(派生部分が切り落とされることを「スラッシング」と言いま
す)。
仮想関数を正常に機能させるためには、list には Component のポインタか参照を格納し
なければなりません。


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

#include <iostream>
#include <string>
#include <list>

using namespace std;

class Component {
public:
string name;
Component() { this->name = "; }
Component(string name) { this->name = name; }

virtual void add(Component* c){};
virtual void remove(Component* c){};
virtual void display(){};
};

class Composite : public Component {
private:
list<Component*> children;
public:
Composite(string name) : Component(name){};
void add(Component* c) { children.push_back(c); }
void remove(Component* c) {
for ( list<Component*>::iterator p = children.begin();
p != children.end(); ++p) {
if (c == *p ) { children.erase(p); break; }
}
}
void display() {
cout << this->name << endl;
for ( list<Component*>::iterator p = children.begin();
p != children.end(); ++p) {
(*p)->display(); //☆
}
}
};

class Leaf : public Component {
public:
Leaf(string name):Component(name){}
void add(Component c){}
void remove(Component c){}
void display() { cout << name << endl; }
};

int main() {

Component *root = new Composite(root);
Component *n01 = new Leaf(01);
Component *n02 = new Leaf(02);
root->add(n01);
root->add(n02);

Component *compA = new Composite(compA);
Component *A01 = new Leaf(A1);
Component *A02 = new Leaf(A2);
compA->add(A01);
compA->add(A02);
root->add(compA);

Component *compB = new Composite(compB);
Component *B01 = new Leaf(B1);
Component *B02 = new Leaf(B2);
Component *B03 = new Leaf(B3);

compB->add(B01);
compB->add(B02);
compB->add(B03);
root->add(compB);

root->display();

delete root;
delete compA;
delete compB;
delete n01;
delete n02;
delete A01;
delete A02;
delete B01;
delete B02;
delete B03;

return 0;
}


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

× スラッシング
○ スライシング


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

もしかしてですが、デザインパターンの勉強とC++言語の勉強を並行して
やってませんか?
C++言語でデザインパターンの勉強をするのであれば、C++言語の文法に関しては
大丈夫な状態にしておかないと問題点が絞り込めずに余計混乱すると思います。
少なくとも基本的な文法に関してはきちんと理解してからデザインパターンの
勉強に入ったほうが良いと思います。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

今回で言うなら仮想関数をうまく使うためには派生元のクラスの
ポインタか参照で扱う必要があると言う部分は基本的な知識になります。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

蛇足ですけれど、

VC.net2005ではなくてVC++2005だと思います。
2005以降は開発環境名に.netは入ってませんので。


返信引用
たいちう
 たいちう
(@たいちう)
ゲスト
結合: 23年前
投稿: 662
 

επιστημηさん、お忘れ物です。
virtual ~Component() {}

ついでに(用途にもよるけど)これも加えて、
root以外のdeleteもなくしたいところ。

~Composite() {
for ( list<Component*>::iterator p = children.begin();
p != children.end(); ++p) {
delete *p;
}
}


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

おー。ふぉろーありがとです。


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

あ、Componentのメソッド群は pure virtual にするが吉かな。


返信引用
たに
 たに
(@たに)
ゲスト
結合: 17年前
投稿: 13
Topic starter  

お早い回答ありがとうございます。
無事に動作する様になりました。ホント助かりました。

◎シャノンさん
>仮想関数を正常に機能させるためには、list には Component のポインタか参照を
>格納しなければなりません。

なるほど。これが原因ですか・・・。でもエラーにはならないんですね。

◎επιστημηさん
ソース修正ありがとうございます。
ご丁寧にインデントまで直して頂いて・・・。

◎PATIOさん
>もしかしてですが、デザインパターンの勉強とC++言語の勉強を並行して
>やってませんか?
お察しの通りです。恥ずかしながら今までCメインでやってまして、
C++も形だけクラス使ってソース書いてたりはしたのですが、
継承だの仮想関数だのは殆ど使った事がなくて・・・ホントお恥ずかしい
限りです。

>VC.net2005ではなくてVC++2005だと思います。
よくよく見たらそうですね。2005も今回初めて使ってます。
ずっとVC++6.0だったもので・・・。また勝手が大分変わりましたね。
使い易くなってる気はしますけど。

◎たいちうさん
>root以外のdeleteもなくしたいところ。
なるほど・・・全然考えも及びませんでした。
そっちの方が遥かにスマートですね・・・。
別の意味で勉強になります。

お礼のコメントも長くなってしまいましたが、
皆さんご丁寧にありがとうございました。精進致します。


返信引用
たに
 たに
(@たに)
ゲスト
結合: 17年前
投稿: 13
Topic starter  

remove()の件をすっかり忘れてました・・・
現状だと第一階層にある物しか削除出来ませんね。
要素が枝なのか葉なのかを判別する仕組みが
必要だと思ってますが、その認識で良いのでしょうか?


返信引用
たに
 たに
(@たに)
ゲスト
結合: 17年前
投稿: 13
Topic starter  

こんな感じで、任意のオブジェクトの削除が出来ました。
ありがとうございました。

void remove(Node* c) {
for ( list<Node*>::iterator p = children.begin();
p != children.end(); ++p)
{
if (c->name == (*p)->name){ children.erase(p); return;}
else (*p)->remove(c);
}
}


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

同じ名前のノードが複数あったらアウト。


返信引用
たに
 たに
(@たに)
ゲスト
結合: 17年前
投稿: 13
Topic starter  

>同じ名前のノードが複数あったらアウト。

あ。確かにそうですね。
全て回りきるまでreturnしないようにすれば、
全部削除出来ますが、特定の場所のだけになると
また別途処理が必要になりますね。

またファイルとフォルダを想定した場合に
同じファイル名が同じ階層にある事はないですが、
ファイルとフォルダ名が同一って事も
考えられますね・・・。
そうなると、add()の時にフォルダなのか
ファイルなのかを判定しないとならないですね・・・

そうなるとファイルかフォルダの別をメンバに
加えてやって判定する形でしょうか。


返信引用
固定ページ 1 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました