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

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

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

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

Q&A

解決済

1回答

229閲覧

ポインタ変数を引数にとるstructを作成するとポインタ変数の値が書き換わる

idonotknow

総合スコア74

C++

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

0グッド

0クリップ

投稿2019/06/29 11:30

編集2019/06/29 16:22

#前提・実現したいこと
以下のようにStackインターフェイスに準拠した、内部にLinkedList構造体を使用したLLStack構造体を作成しました。
#該当のソースコード

c++

1namespace mystd 2{ 3 template <typename T> struct Stack 4 { 5 virtual ~Stack() {}; 6 virtual void push(T val) = 0; 7 virtual std::optional<T> pop() = 0; 8 }; 9 10 template <typename Element> struct LinkedList 11 { 12 LinkedList<Element>* next; 13 const Element value; 14 15 LinkedList(LinkedList<Element>* next, Element value) 16 :next(next), value(value){} 17 }; 18 19 template <typename Element> struct LLStack : Stack<Element> 20 { 21 LLStack(): head(nullptr){} 22 23 void push(Element ele) 24 { 25 LinkedList<Element> new_head = LinkedList(head, ele); 26 head = &new_head; 27 } 28 29 std::optional<Element> pop() 30 { 31 if (head == nullptr) 32 { 33 return std::nullopt; 34 } 35 auto ele = head->value; 36 head = head->next; 37 38 return ele; 39 } 40 41 private: 42 LinkedList<Element>* head; 43 }; 44}

以下のように実行したところ、2回目のpop()でclangでSegmentation fault、gccでは意図しない値が出力されてしまいました。

c++

1int main() 2{ 3 mystd::LLStack<int> lStack{}; 4 lStack.push(111); 5 lStack.push(222); 6 7 auto lp = lStack.pop(); 8 if (lp.has_value()) { 9 std::cout << "LLStack.pop: " << lp.value() << "\n"; 10 } else { 11 std::cout << "LLStack.pop: null" << "\n"; 12 } 13 14 lp = lStack.pop(); 15 if (lp.has_value()) { 16 std::cout << "LLStack.pop: " << lp.value() << "\n"; 17 } else { 18 std::cout << "LLStack.pop: null" << "\n"; 19 } 20}

出力結果(gcc):

LLStack.pop: 222
LLStack.pop: -1793214720

#試したこと

デバッグ出力を色々な箇所に差し込んだ結果、LLStack構造体のhead変数がLinkedList生成のタイミングでなぜか置き換わっていることがわかりました(理由はわかりませんでした)。

c++

1void push(Element ele) 2{ 3 if(head) { 4 std::cout << "\t" << "currenthead.value: " << head->value << "\n"; 5 } 6 LinkedList<Element> new_head = LinkedList(head, ele); 7 if(head) { 8 std::cout << "\t" << "currenthead.value: " << head->value << "\n\n"; 9 } 10 11 head = &new_head; 12}

上のコードを呼んだ結果:

currenthead.value: 111 currenthead.value: 222

また、単独でLinkedListを使って同じような処理をしてみましたが、再現しませんでした。

c++

1int main() { 2 mystd::LinkedList<int> *head = nullptr; 3 auto new_head = mystd::LinkedList<int>(head, 11); 4 head = &new_head; 5 std::cout << "new_head: " << head->value << "\n"; 6 7 auto new_new_head = mystd::LinkedList<int>(head, 22); 8 head = &new_new_head; 9 std::cout << "newnew_head: " << head->value << "\n"; 10 std::cout << "newnew_head->next->value: " << head->next->value << "\n"; 11}

出力結果:

new_head: 11
newnew_head: 22
newnew_head->next->value: 11

#補足情報
↓こちらに実行可能なコードがあります
Wancobox

#追記
生ポインタからunique_ptrに変更しました。

c++

1template <typename Element> struct LinkedList 2{ 3 std::unique_ptr<LinkedList<Element>> next; 4 const Element value; 5 6 LinkedList(LinkedList<Element> next, Element value) 7 :next(new LinkedList<Element>(next)), value(value){} 8 LinkedList(std::unique_ptr<LinkedList<Element>> next, Element value) 9 :next(std::move(next)), value(value){} 10}; 11 12template <typename Element> struct LLStack : Stack<Element> 13{ 14 LLStack(): head(nullptr){} 15 16 void push(Element ele) 17 { 18 head.reset(new LinkedList<Element>(std::move(head), ele)); 19 } 20 21 std::optional<Element> pop() 22 { 23 if (head == nullptr) 24 { 25 return std::nullopt; 26 } 27 auto ele = head->value; 28 head = std::move(head->next); 29 30 return ele; 31 } 32 33 private: 34 std::unique_ptr<LinkedList<Element>> head; 35};

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

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

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

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

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

guest

回答1

0

ベストアンサー

C++

template <typename Element> struct LLStack : Stack<Element>
{
...
void push(Element ele)
{
LinkedList<Element> new_head = LinkedList(head, ele);
head = &new_head;
}

new_headの生存期間は関数内ですから、脱出以降headの参照先にデータがある保証はありません。

投稿2019/06/29 11:37

LouiS0616

総合スコア35658

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

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

idonotknow

2019/06/29 12:26

ご返答ありがとうございます。 >new_headの生存期間は関数内ですから、脱出以降headの参照先にデータがある保証はありません。 ということは、このようにスタックを実装したい場合、LinkedList構造体のポインタだけでなく実体もどこか(おそらくLinkedList構造体の中?)に保持しておくしかないのでしょうか。
LouiS0616

2019/06/29 12:37

いえ、その方法は困難でしょう。 unique_ptrを使うのがおそらく一番簡単です。 newで動的にメモリを確保しても良いですが、その場合適切なタイミングで解放してやらないといけません。
idonotknow

2019/06/29 16:23

>unique_ptrを使うのがおそらく一番簡単です。 質問に追記したようにunique_ptrを使うように修正したところ機能するようになりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問