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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++

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

Q&A

5回答

2466閲覧

C++のテンプレートでのツリー構造での質問です。

meronobu

総合スコア7

C++

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

0グッド

0クリップ

投稿2016/06/05 13:01

編集2016/06/05 13:03

こんばんは。
C++のテンプレートでの質問です。

template<class T,class U>
class folderItem{

public:
folderItem(T parent = NULL)
{
m_parent = parent;
}

void InsertChild(U item);

private:
T m_parent;
std::vector<U> m_children;
}

template<class T,class U>
class fileItem : public folderItem<T,U>{

public:
fileItem(T parent) : folderItem<T,U>(parent)
{
}
}

上記のような、親子関係を持つテンプレートクラスを作りたいのですが、インスタンスの生成時に困っています。

(理想)
folderItem<folderItem,fileItem> item;
folderItem<folderItem,folderItem> item;
(現実)
fileItem< folderItem< folderItem<.........,fileItem<folderItem<folderItem ........
のようになってしまい、最初の型定義でつまずいてしまいます。

いままでは、テンプレートを使わずに親も子も基盤クラスで親子関係を作り、必要なときにキャストしていたのですがキャストばかりになってしまい読みにくくなってきましたのでテンプレート化して読みやすくしたくなった所存です。

どなたか助けて下さい。
よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

データ構造の親子関係(ツリー構造)とクラスの親子関係(基底・派生)を混同しているように思います。クラステンプレートは、テンプレート引数が異なると互換性のない別のクラスとして認識されるので、例えば、あるファイル・フォルダーを別の枝に付け替えようとすると、型(テンプレート引数)を変えなくてはなりません。キャストどころの話ではなくなります。

前のやり方で、キャストはともかくうまく動いていたならそのままで良いと思います。
C++の特性上、キャストは仕方ないと思います。

もし、どうしてもキャストをなくしたいというなら、いっそフォルダーとファイルの操作をすべて含んだクラスにするという方法もあります。良い設計かどうかは微妙ですが。

投稿2016/06/06 03:27

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2016/06/07 02:23

+1; クラス間の静的/コンパイル時の関係性 と オブジェクト間の動的/実行時の関係性 を混同されているように思えます。 質問者さんは デザインパターン/Compositeパターン を調べると幸せになれるかも…
catsforepaw

2016/06/07 02:58

指摘としてはyohhoyさんの方が適切ですね。言いたかったのはそれです。 ただ、すでにツリー構造は実装できていたようなので、Compositeパターン自体は理解されているのだと思います。 C++はコンパイル時に型が特定されている必要があり、基底クラスのポインタで派生クラスのメンバにアクセスするにはダウンキャストが不可欠ですが、質問者さんはそれを煩わしいと思っているようです。
guest

0

他の人が書いてる通り,根本的に設計が間違っているのだけれど,無理やりこのままの設計でやってみるとする.

理想から察するに,あるFolderItem branchは別のFolderItem rootの子で,それ自身の子としていくつかのFileItemを持つように作りたいのだろうけれど…

  • トップレベルのFolderItem rootを作るには,親にはnullptr_tを置くとして,どうしても一つ下のFolderItem branchの完全型が必要
  • FolderItem branchを作るためには,親となるFolderItem rootの完全型と,子となるFileItemの完全型が必要
  • 一番末端のFileItemは,子供の型はnullptr_tにすればいいとしても,親となるFolderItem branchの完全型が必要

となっていて,どうやっても型定義ができないように思える.
素直に設計を見直すのが手っ取り早いし,見直すべきコードだと思う.

投稿2016/06/06 20:42

tamy

総合スコア442

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

fileはchildrenを持たなくて良いので、m_childrenを持つクラスを親とするのは不適切です。

ファイルとフォルダに共通するのは「親フォルダがいる」ことなので、この機能をファイル・フォルダ共通の親としたほうがうまくいきそうです。

Chironianさんの回答と名前・構造がかぶりますが以下の様な感じです。
(コンパイルしてませんので構文エラーがあるかもしれません。)

C++

1class DiskItem { 2 public: 3 DiskItem(DiskItem* parent) { 4 m_parent = parent; 5 } 6 private: 7 DiskItem* m_parent; 8} 9 10class Folder : public DiskItem { 11 public: 12 Folder(DiskItem* p) : DiskItem(p) {} 13 void InsertChild(DiskItem* child); 14 private: 15 std::vector<DiskItem*> m_children; 16} 17 18class File : public DiskItem { 19 public: 20 Folder(DiskItem* p) : DiskItem(p) {} 21}

と、このままではDiskItem→Folder/Fileのキャストをせねばならず、振り出しに戻ってしまいますが…。

この場合、誰かにキャストしてもらう(ダウンキャスト済みの参照を返す機能を作る)という手があります。
この時、is_instanceofみたいなことをやってもいいですが美しくないので、親クラスに実体を区別できる属性(例えばisFolder)を持たせるのが常套手段かと思います。
区別するための属性に関するコードはChironianさんが回答済みなので割愛です。

与太話ですが、ダウンキャストをしなきゃいけない親子関係はイマイチですぞ。という話しがあります。
今回でいうと、DiskItemという親を持たせるのがいいのか?というあたりでしょうか。
m_parentはFolderでいいのでは?DiskItemっていらんくね?
でもDiskItemいなかったらFile、Folderが両方m_parentを持つことになってダサくね?
という感じですが、これ以上は設計ポリシーに関する問題な気がしますのでこの辺りで止めます。
もっといい方法もあると思いますし(^^;

ということで、テンプレートではなくキャストをどこか一箇所にまとめてみては?というのが私の提案です。

投稿2016/06/05 23:54

tnd-.-b

総合スコア247

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは。

どうも根本的な発想が宜しくないような気がします。

クラスの継承は通常is_a関係です。「派生クラスは基底クラスの一種」という関係がある時に派生するとうまく行くことが多いです。
今、meronobuさんは、「フォルダはフォルダの一種」、「ファイルもフォルダの一種」という使い方をしようとしているようです。

私なら、たぶん下記のような方向で考えるだろうと思います。

C++

1class DiskItem 2{ 3 string mPath; 4 bool mIsFolder; 5public: 6 DiskItem(string iPath, bool iIsFolder) : 7 mPath(iPath), 8 mIsFolder(iIsFolder) 9 { } 10 11 bool isFolder() const {return mIsFolder;} 12}; 13 14class Folder : public DiskItem 15{ 16 vector<DiskItem*> mChildList; 17public: 18 Folder(string iPath) : DiskItem(iPath, ture) { } 19 void addDiskItem(DiskItem* iDiskItem) 20 { 21 mChildList.push_back(iDiskItem); 22 } 23}; 24 25class File : public DiskItem 26{ 27public: 28 File(string iPath) : DiskItem(iPath, false) { } 29};

投稿2016/06/05 13:59

編集2016/06/05 14:01
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

private:
T m_parent;
std::vector<U> m_children;

子ノードが親ノードの実体を、親ノードがすべての子ノードの実体を持とうとするならば、循環矛盾が生じると思うけど・・・

投稿2016/06/05 13:35

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問