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

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

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

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

Q&A

解決済

5回答

1161閲覧

テンプレートクラスの基礎の部分が分かりません。

iwanami

総合スコア9

C++

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

0グッド

0クリップ

投稿2020/03/27 10:26

以下はある参考書で使用されているプログラムです。
テンプレートクラスの説明をしているのですが、一部説明がなく、ネットで検索しても出てこなかったのでこちらで質問させて頂きました。

以下の(あ)のコードが急に出てきて何を表しているのか具体的に分かりません。
テンプレートクラスのメンバ変数なら、「data_t next;」とすればいいはずなのに、listの型を使用しています。
そして(い)でもlist型の戻り値を返す関数を作成しています。
(う)(え)ではlist型でオブジェクトとポインタを宣言しています。
ただ、今まで出て来た参考書の説明では(う)(え)の「<char>」の部分は、data_tの型にしか掛かっていませんでした。
なので<char>は非公開メンバのdataか、公開メンバのgetdata()にしか掛かってないと思っていました。
ですがこのプログラムでは
nextや*getnext()にもかかっています。

ちょっと話が分かりにくいかもしれませんが、要は(あ)が何を表しているのか知りたいのです。
回答宜しくお願いします。

// 単純な汎用リンクリスト
#include <iostream>
using namespace std;

template <class data_t> class list {
data_t data;
list *next;   ・・・・・・(あ)
public:
list(data_t d);
void add(list *node) {node->next = this; next = 0; }
list *getnext() { return next; }   ・・・・・(い)
data_t getdata() { return data; }
};

template <class data_t> list<data_t>::list(data_t d)
{
data = d;
next = 0;
}

int main()
{
list<char> start('a');     ・・・・・・(う)
list<char> *p, *last;     ・・・・・・(え)
int i;

// リストを作成する
last = &start;

for(i=1; i<26; i++) {
p = new list<char> ('a' + i);
p->add(last);
last = p;
}

// リストを追跡する
p = &start;

while(p) {
cout << p->getdata();
p = p->getnext();       ・・・・・・(お)
}
cout << "\n";

return 0;
}

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

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

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

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

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

guest

回答5

0

ベストアンサー

テンプレートは全く関係ありませんね。
リスト構造が理解できていないだけです。

リストとは、データに次のデータの位置を付け加えて、データをつないでいくもの
だと思っていますね。
'a'、'b'、'c' をつなぎたい。
'a' というデータに、次の 'b' の位置を付け加える。
'b' というデータに、次の 'c' の位置を付け加える。
'c' というデータに、次のデータはないので 0 を付け加える。

データの型は char。次のデータの位置は char *。だから、

C++

1class list { 2 char data; 3 char *next; 4}

でいいじゃないか。

なぜ、char *next; でなく list *next; でないといけないのか?
という質問ですね。

先頭の start に 'a' というデータと 'b'の位置が入っているとすれば、
start.next が char * だから、*start.next で 'b' というデータは取り出せます。

ところが、'b' というデータ自身からは、'c' の位置が入っている next を
取り出せません。

start.next が list* だったら、*start.next が
「'b' と 'c' の位置が入っている list という箱」になるので、
(*start.next).data で 'b' が取り出せ、
(*start.next).next で 'c' の位置が取り出せます。
(*start.next).data は start.next->data、
(*start.next).next は start.next->next と書けます。

p = &start; で p は 「'a' と 'b' の位置」の両方が入った箱を指します。
p->getdata() は p->data であり 'a' です。 
p->getnext() は p->next であり、'b' の位置です。
p = p->getnext() で p は 「'b' と 'c' の位置」の両方が入った箱を指します。

だから、next は char * ではなく、list * でなければならないのです。

もう一度繰り返します。
class list を、「データと次のデータの位置が入った箱」にしたくても、実際には
「データと次の箱の位置が入った箱」でないと、データを順に取り出すことができないのです。

投稿2020/03/29 04:09

kazuma-s

総合スコア8224

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

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

iwanami

2020/03/29 06:24

回答ありがとうございます。 自分の分からない所を的確にまとめてくださりありがとうございます。 漠然と想像していたリスト構造を具体的に教えてくださり助かりました。
guest

0

data_t *nextと書くところを

そんな謎の思い込みは捨ててください

data_tが例えばintだったとして

int data;
int* next;

だったらリストにならないでしょう。

投稿2020/03/28 08:12

ozwk

総合スコア13553

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

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

iwanami

2020/03/28 08:39

回答ありがとうございます。 そこが曖昧かもしれません。 僕はその形でもリストは出来ると思っているのですが、*next はオブジェクト自身の(言い方が適切ではないかもしれませんが)クラス型でないと、リストは出来ないのでしょうか。
ozwk

2020/03/28 09:11 編集

nextの型がint(のポインタ)だったら nextのnextが存在しないじゃないですか それだとリストじゃなくてただの値を2つメンバに持つ型です
iwanami

2020/03/28 09:18 編集

>>1つ目の回答に関して ozwkさんが言っていた事について考えていたのですが、このプログラムの start や p、last と言ったポインタは確かにchar型だと成り立たないですね。 それでlist自身のクラスの型で宣言しているとすると、少し分かった気がします。 >>2つ目の回答に関して (時間差で返信にズレを生じさせてしまいましたが。) 確かにそれだと next はただint型 の値を指しているポインタに過ぎないですね。
guest

0

まずリスト構造、ここでは単方向リストがどんな構造か理解していますか?
(単方向)リストは複数の要素を数珠つなぎにしたデータ構造です。
件の list<data_t> は数珠つなぎにされた一つ一つの数珠玉を表現しています。

この数珠玉は

  • 数珠玉に納められた要素: data_t data;
  • 次の数珠玉を指すポインタ: list *next; 

がそのメンバとして用意されています。
data_t getdata() で要素(の値)を返し、
list *getnext() が次の数珠玉(へのポインタ)を返します。

[追記] list<char> をそれと等価な list_of_char に置き換えました。
これでわからんとしたら、templateがわからんわけじゃない。

C++

1typedef char data_t; 2 3class list_of_char { 4 data_t data; 5 list_of_char *next; // ・・・・・・(あ) 6public: 7 list_of_char(data_t d); 8 void add(list_of_char *node) {node->next = this; next = 0; } // nodeの次にthisをつなぐ 9 list_of_char *getnext() { return next; } // ・・・・・(い) 10 data_t getdata() { return data; } 11}; 12 13list_of_char::list_of_char(data_t d) { 14 data = d; 15 next = 0; 16} 17 18#include <iostream> 19using namespace std; 20 21int main() { 22 list_of_char start('a'); // ・・・・・・(う) 23 list_of_char *p, *last; // ・・・・・・(え) 24 int i; 25 // リストを作成する 26 last = &start; 27 for(i=1; i<26; i++) { 28 p = new list_of_char('a' + i); 29 p->add(last); 30 last = p; 31 } 32 // リストを追跡する 33 p = &start; 34 35 while(p) { 36 cout << p->getdata(); 37 p = p->getnext(); // ・・・・・・(お) 38 } 39 cout << "\n"; 40 return 0; 41}

投稿2020/03/27 10:53

編集2020/03/28 23:46
episteme

総合スコア16612

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

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

iwanami

2020/03/27 14:08

回答ありがとうございます。 リストの流れに関しては、手こずりましたが一応理解出来ていると思います。 テンプレートになって型が流動的になったので、少し理解しづらい所もありましたが、一応理解出来ていると思います。
episteme

2020/03/27 23:40

ならば何が疑問なんです?
iwanami

2020/03/28 07:36 編集

(あ)が何を表しているのか知りたいです。 今までの参考書の説明だと、data_t *next;と書く所を、list *next;と書いてるのが分かりませんでした。 SaitoAtsushiさんの回答で、list *next;はlist<data_t> *next;の省略だという事を知って、自分なりに考えてみたんですが、ポインタの時にlist *next;と記述するのかと思っているのですが。
episteme

2020/03/28 08:10 編集

struct list_of_int { // intを保持する数珠玉 int data; // この数珠玉が保持するデータ list_of_int* next; // 次の数珠玉へのポインタ }; ならわかります?
iwanami

2020/03/28 08:34

分からないです。 コンストラクタがクラス名と同じ名前の関数を使うので、それと似たようなものなのかと思っていますが。
episteme

2020/03/29 04:11

追記しました。
iwanami

2020/03/29 06:20

追記確認しました。 これはテンプレート構造を抜かしたリスト構造ですよね。 これを見るとテンプレート関係なくリスト構造が理解出来ていなかったのが分かります。 epistemeさんは最初からこの事を指摘していたんですね。
guest

0

list *next; という宣言は list<data_t> *next; と書いた場合と同じです。 あるクラステンプレートの定義の中にそのクラステンプレート自身と同じ名前が現れた場合には型引数を省略できるというルールがあります。

data_t *next; ではなく list *next; であるのは、リスト (より詳しく言うなら片方向の連結リスト) というのはそういうものだとしか言いようがないのでリストのデータ構造について調べてみてください。 Wikipedia などに詳しい説明があります。

ここで定義している list というクラスはこれがリスト全体なわけではなくてリストのノードひとつを表しているということを念頭に置いて考えると分かりやすいかと思います。

投稿2020/03/27 10:42

編集2020/03/27 10:50
SaitoAtsushi

総合スコア5684

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

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

iwanami

2020/03/28 08:40 編集

回答ありがとうございます。 >>list *next; という宣言は list<data_t> *next; と書いた場合と同じです。 あるクラステンプレートの定義の中にそのクラステンプレート自身と同じ名前が現れた場合には型引数を省略できるというルールがあります。 これは初めて知りました。知れて良よかったです。ありがとうございます。 >>data_t *next; ではなく list *next; であるのは、リスト (より詳しく言うなら片方向の連結リスト) というのはそういうものだとしか言いようがないのでリストのデータ構造について調べてみてください。 Wikipedia などに詳しい説明があります。 てきとうではなく何か理由があるんですね。自分なりに調べてみたんですが、ポインタだと list *next と宣言するのではないかと思っているんですが。 >>ここで定義している list というクラスはこれがリスト全体なわけではなくてリストのノードひとつを表しているということを念頭に置いて考えると分かりやすいかと思います。 一応リストの処理の流れは理解しているつもりです。今までは型が固定されたポインタでしたが、テンプレートになって、型が流動的になったので、ちょっと捉えにくいという所はありましたが、処理の流れは理解しているつもりです。
guest

0

皆様回答ありがとうございました。
どの回答も助かりましたが、次に見に来る人の事を考えて、問題点とリスト構造の仕組みを具体的に書き出してくれたkazuma-sさんにベストアンサーを付けたいと思います。

投稿2020/03/29 06:30

iwanami

総合スコア9

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問