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

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

ただいまの
回答率

88.37%

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

受付中

回答 5

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,746

meronobu

score 7

こんばんは。
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 ........
のようになってしまい、最初の型定義でつまずいてしまいます。

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

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

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 5

+1

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

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/07 11:23

    +1; クラス間の静的/コンパイル時の関係性 と オブジェクト間の動的/実行時の関係性 を混同されているように思えます。

    質問者さんは デザインパターン/Compositeパターン を調べると幸せになれるかも…

    キャンセル

  • 2016/06/07 11:58

    指摘としてはyohhoyさんの方が適切ですね。言いたかったのはそれです。

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

    キャンセル

0

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

こんにちは。

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

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

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

class DiskItem
{
    string mPath;
    bool mIsFolder;
public:
    DiskItem(string iPath, bool iIsFolder) : 
        mPath(iPath),
        mIsFolder(iIsFolder)
    { }

    bool isFolder() const {return mIsFolder;}
};

class Folder : public DiskItem
{
    vector<DiskItem*> mChildList;
public:
    Folder(string iPath) : DiskItem(iPath, ture) { }
    void addDiskItem(DiskItem* iDiskItem)
    {
        mChildList.push_back(iDiskItem);
    }
};

class File : public DiskItem
{
public:
    File(string iPath) : DiskItem(iPath, false) { }
};

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

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

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

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

class DiskItem {
 public:
  DiskItem(DiskItem* parent) {
    m_parent = parent;
  }
 private:
  DiskItem* m_parent;
}

class Folder : public DiskItem {
 public:
  Folder(DiskItem* p) : DiskItem(p) {}
  void InsertChild(DiskItem* child);
 private:
  std::vector<DiskItem*> m_children;
}

class File : public DiskItem {
 public:
  Folder(DiskItem* p) : DiskItem(p) {}
}

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

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

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

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

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

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • ただいまの回答率 88.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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