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

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

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

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

Q&A

解決済

1回答

4827閲覧

C++ 配列へ代入が出来たり出来なかったりする違いがわからない

opyon

総合スコア1009

C++

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

0グッド

0クリップ

投稿2018/11/11 15:21

編集2018/11/11 18:59

###解決!(タイプミスでした^^;)

C++

1#include <bits/stdc++.h> 2 3class CBTree 4{ 5 private: 6 int heap_size = 0; 7 const int lowerlimit = -1; 8 std::vector<int> heap; 9 10 void print_key_() 11 { 12 for (int i = 1; i <= heap_size; ++i) 13 { 14 std::cout << " " << heap[i]; 15 } 16 std::cout << "\n"; 17 } 18 19 int node_L(const int &idx) 20 { 21 if (idx * 2 <= heap_size) 22 { 23 return idx * 2; 24 } 25 return 0; 26 } 27 28 int node_R(const int &idx) 29 { 30 if (idx * 2 + 1 <= heap_size) 31 { 32 return idx * 2 + 1; 33 } 34 return 0; 35 } 36 37 void maxHeapify_(const int &x) 38 { 39 int L = node_L(x); 40 int ans = heap[L] < heap[x] ? x : L; 41 if (heap_size >= 3) 42 { 43 int R = node_R(x); 44 ans = heap[ans] < heap[R] ? R : ans; 45 } 46 if (heap[ans] != heap[x]) 47 { 48 std::swap(heap[ans], heap[x]); 49 maxHeapify_(ans); 50 } 51 } 52 53 public: 54 CBTree() 55 { 56 heap.resize(2000001, 0); 57 heap[0] = lowerlimit; 58 } 59 60 void push(int key) 61 { 62 heap[++heap_size] = key; 63 if (heap[1] < key) 64 { 65 std::swap(heap[1], heap[heap_size]); 66 } 67 68 int i = heap_size; 69 while (i > 1 && heap[i / 2] < heap[i]) 70 { 71 std::swap(heap[i / 2], heap[i]); 72 i /= 2; 73 } 74 } 75 76 int top() 77 { 78 return heap[1]; 79 } 80 void pop() 81 { 82 heap[1] = heap[heap_size]; 83 --heap_size; 84 maxHeapify_(1); 85 } 86 void print_key() 87 { 88 print_key_(); 89 } 90}; 91 92void alds1_9_3() 93{ 94 // 完全二分木 95 // Complete binary tree 96 // 優先順位キュー 97 // priority queue 98 CBTree T; 99 100 std::string s; 101 int key; 102 while (s != "end") 103 { 104 std::cin >> s; 105 if (s == "insert") 106 { 107 std::cin >> key; 108 T.push(key); 109 } 110 else if (s == "extract") 111 { 112 std::cout << T.top() << "\n"; 113 T.pop(); 114 } 115 // デバッグ用 116 // T.print_key(); 117 } 118} 119 120int main() 121{ 122 if (0) 123 { 124 std::cin.tie(0); 125 std::ios::sync_with_stdio(false); 126 } 127 alds1_9_3(); 128 getchar(); 129 return 0; 130} 131 132// https://onlinejudge.u-aizu.ac.jp/problems/ALDS1_9_C 133 134// 入力例 1 135// insert 8 136// insert 2 137// extract 138// insert 10 139// extract 140// insert 11 141// extract 142// extract 143// end 144 145// 出力例 1 146// 8 147// 10 148// 11 149// 2

###知りたいこと
どこが間違っているのかご教示頂けると助かります。

###現状・試したこと
T.push()メソッド経由で配列に数値を代入出来ないのに、
T.heap[2]=222などで直接配列に代入すると正常に更新出来ます

処理途中にデバッグ出力してみたのですが 配列名[インデックス] = 数値で,
同じように代入しているのに代入されていない意味が理解できません。

型の不一致を避けるために全てint型に変更しましたが同じです。

T.push(111); prt(T.heap[1]); T.heap[2] = 222; prt(T.heap[2]);

※以下マクロを使用しています。見づらいコードですみません。
prt()
prt2()

###サンプルコード(質問で動作させられる部分のみ)

C++

1#include <bits/stdc++.h> 2#define prt(x) std::cout << x << std::endl; 3#define prt2(x) std::cout << " " << x; 4 5class CBTree 6{ 7 public: 8 int heap_size; 9 const int lowerlimit = -1; 10 int heap[100]; 11 12 CBTree() 13 { 14 heap_size = 0; 15 heap[0] = lowerlimit; 16 } 17 void push(int key) 18 { 19 // debug 20 21 prt2("heap[0]:") 22 prt(heap[0]); 23 24 ++heap_size; 25 heap[heap_size] == key; 26 27 // heap[1] == key; 28 // 要素番号を直接指定しても同じ 29 30 prt2("heap_size:"); 31 prt2(heap_size); 32 prt2("heap[i]:"); 33 prt2(heap[heap_size]); 34 prt2("key:"); 35 prt2(key); 36 prt(""); 37 38 // debug 39 } 40}; 41 42void alds1_9_3() 43{ 44 // 完全二分木 45 // Complete binary tree 46 // 優先順位キュー 47 // priority queue 48 CBTree T; 49 50 std::string s; 51 int key; 52 53 T.push(111); 54 prt(T.heap[1]); 55 56 T.heap[2] = 222; 57 prt(T.heap[2]); 58} 59 60int main() 61{ 62 alds1_9_3(); 63 getchar(); 64 return 0; 65} 66

期待する出力

heap[0]:-1 heap_size: 1 heap[i]: 111 key: 111 111 222

現状の出力(32761の値は不定)

heap[0]:-1 heap_size: 1 heap[i]: 32761 key: 111 32761 222

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

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

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

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

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

guest

回答1

0

ベストアンサー

タイポです。
heap[heap_size] == key;heap[heap_size] = key;

追記

回答があまりに端的になったので、他にも気になったことをつらつら書きます。

マクロの使い方
マクロでしかできないことは、あまり多くありません。
他の方法で充分且つシンプルに代替できるときはそれを利用してください。

C++

1template<typename T> 2void print(const T& arg) { 3 std::cout << arg << "\n"; 4}

また、どうしてもマクロを使いたいときは、UPPER_SNAIL_CASEで命名するのが一般的です。

C++

1#define PRINT(x) \ 2 std::cout << (x) << "\n"

メンバ変数の取り扱い
可視性は可能な限りprivateにすべきです。
剥き出しのメンバ変数は、グローバル変数と同じように取りまわせてしまうからです。

利用するヘッダ
これに関しては個人的な好みが大いにありますが、
bits/g++.hではなく、必要な標準ヘッダをインクルードした方がシンプルかと。

今回の場合 string と iostream だけで済みそうです。

インクリメントを使いこなす

C++

1++heap_size; 2heap[heap_size] = key;

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

C++

1heap[++heap_size] = key;

これも好みの問題ですが、
せっかく単項インクリメントが用意されているのでその恩恵に預かっても良いのかな、と。

範囲外へのアクセス
他言語と比べて、C/C++での生配列への範囲外アクセスは感知しづらいです。
範囲内であるか前以て判定する処理を書いておけば安心です。


こういう関数を用意してオレオレコーディングを楽しむのもアリと言えばアリです。

C++

1#include <iostream> 2 3namespace py_like { 4 void print() { 5 std::cout << "\n"; 6 } 7 8 template <typename First, typename... Rest> 9 void print(const First& f, const Rest&... r) { 10 std::cout << f << " "; 11 print(r...); 12 } 13} 14 15int main() { 16 py_like::print(1, 2, 3); 17}

実行結果 Wandbox

plain

11 2 3

さらなるおまけ

マクロを使うと、見づらさ以上の弊害があります。
競プロ界隈ではしばしば使われるようですが、リスクは理解しておきたいものです。

C++

1#include <iostream> 2 3#define PRINT_1(x) std::cout << x << "\n"; 4#define PRINT_2(x) std::cout << (x) << "\n"; 5 6template<typename T> 7void print(const T& arg) { 8 std::cout << arg << "\n"; 9} 10 11int main() { 12 print (1 << 2); // 4 13 PRINT_1(1 << 2); // 12 14 PRINT_2(1 << 2); // 4 15}

1 << 2という文字列のまま扱われるのが厄介なんですよね、マクロの場合。
片っ端から括弧を付ければ良いわけですが、手間がメリットに釣り合わないようにも感じられます。

PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う
なお、リンク先で紹介されている『例外』も、C++ならある程度代替可能です。
0. PRE00-C-EX1(ローカル関数) ⇒ ラムダでクロージャを作れば良い
0. PRE00-C-EX3(コンパイル時定数) ⇒ constexpr
0. PRE00-C-EX4(型総称関数) ⇒ template

トークンの連結は、C++でもできそうに無いですが。

投稿2018/11/11 15:25

編集2018/11/12 06:30
LouiS0616

総合スコア35658

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

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

opyon

2018/11/11 15:26

!?
opyon

2018/11/11 15:27

お恥ずかしいです><;
opyon

2018/11/11 15:29

回答ありがとうございます。 1時間くらい悩んでいました^^; 思い込みって怖いですね。
opyon

2018/11/11 18:51

追記ありがとうございます。 回答頂いた後すぐに問題を解いていて追記に気づきませんでした。 >マクロの使い方 仰る通りで極力控えるつもりではいるのですがついつい使ってしまいますね。 問題を解くテンプレなどを使う癖で見づらくてすみません。 >メンバ変数の取扱い 極力privateで分けて必要最小限のpublicのみ公開する意識はしています。 今回はエラーが出て取り急ぎ全てpublicにしてテストしていました。 >利用するヘッダ これもテンプレで極力少ない行数にしたいので1行で済ませてます。 >インクリメント テストの切り分けで外出しにしました。 本コードでは中に入れています。 >範囲外へのアクセス 問題で出題される範囲だけ意識しています。 >こういう関数を用意してオレオレコーディングを楽しむ 一時期マクロテンプレを探してあちこちから拾ってましたがまだそれほど使う機会が無いです。 追々必要になると思うので少しづつマクロ以外のテンプレも増やしたいですね。 後ほどAC通ったコードを追記しておきます。
LouiS0616

2018/11/12 06:06

なるほど、デバッグの一環でコードを変更していたのですね。 マクロについては危険性をしっかり知っておいていただきたいので、また少し追記しました。
asm

2018/11/12 06:16

マクロの危険性というと副作用が何回発生するか分からんという問題が凶悪ですね。 マクロにしか出来ないこと、というのもconstexprや定数式の変更によって大分減ってますし 他の手段を模索した方がいいですね。
LouiS0616

2018/11/12 06:19 編集

#define SQUARE(x) (x) * (x) に対して SQUARE(++x) とかやっちゃうやつですよね。
asm

2018/11/12 06:29

それです。 これを防止するためにも関数形式のマクロと関数はきちんと区別させるべきですね
opyon

2018/11/12 08:18

>さらなるおまけ 追記ありがとうございます。 リンク先は難しい内容で理解するにはまだまだ力不足ですが「マクロが危険」ということは重々承知してるつもりです。 競プロはまだ参加したことないですが主にAOJなどのオンラインジャッジ?での問題に使っています。 定数PIやLL(longlong)やラジアン度変換など滅多に使わないものでもテンプレから1行貼り付けるだけなのでとても便利です。 print系はデバッグ時によく使いますがデバッグ終わると消しています。 どちらにしても個人的に問題を解く時に自己責任で使うものだとは思っています。 今回のご指摘などでこれまで以上に気をつけるきっかけになったのでとても参考になりました。
opyon

2018/11/12 08:30

@asmさんもコメントありがとうございます。 >マクロの危険性というと副作用が何回発生するか分からんという問題が凶悪 >関数形式のマクロと関数はきちんと区別させるべき 副作用もなんとなくやばいやつというイメージしかないのですが一連の回答やコメントから察するに「マクロはやばい」ということだけは肝に銘じておきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問