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

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

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

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

Q&A

解決済

1回答

768閲覧

C++でオブジェクトの参照(ポインタ)を別のクラスに持たせたい

ai2playgame

総合スコア15

C++

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

0グッド

0クリップ

投稿2018/07/25 16:35

編集2018/07/25 16:43

前提・実現したいこと

OpenSiv3Dで簡単なゲームを開発しています。

そこでUIにPlayerオブジェクトのHPを常時表示させたいと考えています。
UIクラスにstd::unique_ptr<Player>変数を持たせ、コンストラクタで生成したオブジェクトを登録しようとしたのですが、そこでコピーが起きているのか、実際はHPが減っているのに、デフォルトの値から変更されません。

何が間違っている & 他に良い手段はないでしょうか?よければ教えてください。

該当のソースコード(一部略)

cpp

1#include <Siv3D.hpp> 2 3class Player 4{ 5private: 6 uint32 m_hp; 7 8public: 9 Player(uint32 hp = 4) 10 :m_hp(hp) {} 11 12 void hp_minus() 13 { 14 this->m_hp--; 15 } 16 17 auto hp() 18 { 19 return this->m_hp; 20 } 21 22 void debug() 23 { 24 Print(U"player.HP"); 25 Print(this->m_hp); 26 } 27}; 28 29class UI 30{ 31private: 32 using PlayerPtr = std::shared_ptr<Player>; 33 PlayerPtr m_playerPtr; 34 35public: 36 UI() = default; 37 UI(Player& player) 38 { 39 m_playerPtr = std::make_shared<Player>(player); 40 } 41 42 void debug() 43 { 44 Print(U"UI.playerPtr->HP"); 45 Print(this->m_playerPtr->hp()); 46 } 47}; 48 49void Main() 50{ 51 Player player(4); 52 UI ui(player); 53 player.debug(); 54 ui.debug(); 55 56 player.hp_minus(); 57 player.debug(); 58 ui.debug(); 59 60 while (System::Update()) 61 { 62 63 } 64}

出力

player.HP
4
UI.playerPtr->HP
4
player.HP
3
UI.playerPtr->HP
4

補足情報

Visual Studio 2017
OpenSiv3D

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

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

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

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

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

guest

回答1

0

ベストアンサー

こんにちは。

std::shared_ptrは原則としてnewで直接確保したオブジェクトだけを管理できます。つまり、静的領域(グローバル変数やstatic変数)やスタック上に獲得したオブジェクトなどを管理することができないのです。
従って、main()で獲得しているPlayer player(4);をstd::shared_ptrで管理することはできません。

できないことをやろうとするとコンパイル・エラーになるように設計されている場合が多いのですが、残念ながらstd::make_sharedはこのケースではエラーを出さずに渡したplayerオブジェクトをコピーして、コピーした方の領域を管理します。

ここを見ると、

型Tに対する shared_ptr<T>オブジェクトを生成し返却する。

このとき、args... で受け取った引数リストを型 Tのコンストラクタへ渡してshared_ptr<T>型のオブジェクトを生成する。

と書かれています。この場合、Playerオブジェクトへの参照を受け取るPlayerのコンストラクタが呼び出されます。
「そんなコンストラクタは定義していないじゃないか!」と言いたいでしょう。確かに明示的には定義されていません。しかし、C++コンパイラは結構多くのケースでコピー・コンストラクタを自動的に定義します。(このおかげで安易にオブジェクトをコピーできるので実にありがたい機能です。)
しかし、今回はそれが意図しない形で働いてしまいました。std::make_sharedは受け取ったplayerオブジェクトをPlayerクラスのコピー・コンストラクタに渡すのでこのタイミングでコピーされていまいます。

UIクラスのコンストラクタでPlayerへの参照を受け取るのではなく、uint32 hpを受け取ると意図通りに動作する筈です。main()関数側はUIオブジェクト内のm_playerPtrをコピーして使うと良いと思います。

(std::shared_ptrをコピーしても、それが管理する領域はコピーされませんのでご安心を。)

投稿2018/07/25 17:24

Chironian

総合スコア23272

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

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

ai2playgame

2018/07/25 18:04 編集

回答有り難うございます。 C++に触れて以来shared_ptrについて、よく分かっていなかったことが理解出来、非常に感謝しています。 ……もう少し、質問よろしいでしょうか? > UIクラスのコンストラクタでPlayerへの参照を受け取るのではなく、uint32 hpを受け取ると意図通りに動作する筈です。main()関数側はUIオブジェクト内のm_playerPtrをコピーして使うと良いと思います。 こちらについて実際どう実装してよいのか分かりません。uit32 hpをUIクラスのコンストラクタ引数としてとるのは分かりますが、それをどうコンストラクタ内で使うのか、main関数内でm_playerPtrのコピーとは何なのか。 加えて今後UIクラスにはPlayer.score等、追加でplayerクラスの情報を表示出来るようにしたいのです。そうなった時にはどうすればよいのか。 重ね重ねになりますが回答頂けると助かります。 (追記)自分なりに考えてみました。 ```cpp #include <Siv3D.hpp> class Player { private: uint32 m_hp; public: Player(uint32 hp = 4) :m_hp(hp) {} void hp_minus() { this->m_hp--; } auto hp() { return this->m_hp; } void debug() { Print(U"player.HP"); Print(this->m_hp); } }; class UI { private: using PlayerPtr = std::shared_ptr<Player>; PlayerPtr m_playerPtr; public: UI() = default; UI(uint32 hp) { m_playerPtr = std::make_shared<Player>(hp); } void debug() { Print(U"UI.playerPtr->HP"); Print(this->m_playerPtr->hp()); } auto playerPtr() { return m_playerPtr; } }; void Main() { UI ui(4); ui.playerPtr() -> debug(); ui.debug(); ui.playerPtr()->hp_minus(); ui.playerPtr()->debug(); ui.debug(); while (System::Update()) { } } ```
Chironian

2018/07/25 19:11

UIのコンストラクタの話はそれでOKです。 main()でコピーを保持する件は、例えば下記です。 int main() {   UI ui(4);   std::shared_ptr<Player> player = ui.playerPtr();   player->debug();   ui.debug();   player->hp_minus();   player->debug();   ui.debug(); 以下略 > 加えて今後UIクラスにはPlayer.score等、追加でplayerクラスの情報を表示出来るようにしたいのです。そうなった時にはどうすればよいのか。 普通に表示すれば良いですけど、何が悩ましいのでしょうか?
ai2playgame

2018/07/28 16:09

確認遅れました。回答有り難うございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問