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

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

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

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

Q&A

解決済

2回答

11879閲覧

C++ のポインタと実体について質問です

kanade

総合スコア23

C++

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

0グッド

0クリップ

投稿2017/02/03 10:40

こんにちは、C++ のポインタと実体についてどうしてもわからないことがあります。

C++ は Java などとは異なり new をしなくても(Player* player = new Player() とせずとも)インスタンスを生成することができると思います(Player player)が、クラスのメンバ変数として所有することを考えた場合、両者にはどのような違いがあるのでしょうか。

ポインタとして所有することには、インスタンス生成のタイミングを遅らせることができたり、前方宣言によりインクルードを減らすことができたりすることなど様々な利点があることは承知しているのですが、new 演算子によるメモリの動的確保にはメモリリークのリスクもあります。

Github などで C++ により開発されているゲームのソースコードなどを調べてみると、インスタンスを実体として保有していたりポインタとして保有していたりと使い分けられていることがわかりました。

また、例えば

C++

1// Point.h 2class Point 3{ 4public: 5 int _x, _y; 6 7 Point(int x, int y) 8 : _x(x) 9 , _y(y) 10 { 11 } 12}; 13 14// MainClass.h 15#include "Point.h" 16class MainClass 17{ 18 Point point1; // 実体 19 Point* point2; // ポインタ 20 21 MainClass() 22 : point1(1, 1) 23 { 24 point2 = new Point(1, 1); 25 } 26 27 ~MainClass() 28 { 29 delete point2; 30 } 31};

とした場合、動きはどちらも同じになるように思います。

加えて、
C++言語、ポインタを使わないプログラミング
こちらのページではポインタによりインスタンスを生成せずにインスタンスを変数として所有することを勧めています。

一体どういうときにはポインタとして持つべきでどういうときには持たざるべきなのか、その基準の目安といったものもわかりません。

周りに教えてくれる人がおらず、用語がおかしかったり、頓珍漢な質問になってしまっていたりしているかもしれませんが教えてくださると助かります。

よろしくお願いします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

こんにちは。

new 演算子によるメモリの動的確保にはメモリリークのリスクもあります。

これが最大の要因です。リークはデバッグが困難なバグの一つです。
他にポインタ自身のメモリも必要になります。例えば、64bits環境(ポインタのサイズが8バイト)で4バイト程度のクラスをポインタで確保するとちょっと勿体無いと感じます。

なので、可能な時は実体で確保した方が好ましい場合が多いです。
実体をコピーせずに引き渡したい時は、参照を使うと安全です。(実体の別名みたいなものです。ポインタに較べて危険な操作をし辛いよう制限されてます。)
参照も使えない時に初めてポインタを使うイメージですね。

とした場合、動きはどちらも同じになるように思います。

MainClassのインスタンスをコピーすると同じになりません。
point2はポインタに設定されているアドレスが単純にコピーされるため、2つのインスタンスを解放するとpoint2が2回解放され、異常動作します。

しかし、この問題は対策があります。std::unique_ptrかstd::shared_ptrを使う、もしくは、コピー・コンストラクタとコピー演算子を実装するのどちらかですね。状況に応じて使い分けます。

ちなみにMainClassで使われている手法はRAIIと呼ばれ、リソース・リークを防ぐための優れた仕組みです。
しかし、悩ましい点が1つあります。一般にデストラクタで例外を投げてはいけませんが、RAIIを多用するとデストラクタでエラーを検出する可能性が高くなります。そのエラーの通知方法の設計が頭が痛いです。

投稿2017/02/03 11:09

編集2017/02/03 11:11
Chironian

総合スコア23272

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

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

kanade

2017/02/03 11:52

ご回答ありがとうございます! いつもありがとうございます。 私が疑問感じていたことが解決されたような気がします。 今までは殆どをポインタを使っていましたが、これからはしっかりと考え実体も積極的に使っていきたいと思います。
guest

0

まず、既存のライブラリに合わせるためにどうしても必要だとか、速度や資源などを極限まで使うようなプログラミングを除いて、そのままポインタを使うこと(特に、オブジェクトをポインタで指すこと)はあまりおすすめできません。C++のクラスで作った、より便利かつ安全なスマートポインタがあるので、そっちを使いましょう(Qiita)。

C++言語特有の「参照を介さないオブジェクト」ですが、特徴として「変数代入などがコピーで処理される」ということがあります。そのため、

  • コピーされても困らない(というよりコピーしたほうが便利な)、上のPointのような値クラス
  • 逆に、コピーすることが不適切となる、何かしらの資源を、ローカルに扱うクラス(スマートポインタ自体のクラスや、ファイルアクセスを行うクラスなど)

のようなものは、直接変数に入れたほうが扱いやすいです。逆に、連結リストや木構造といった、複数箇所からの参照が必要なオブジェクトは、参照なりポインタなりを使うしかありません。

投稿2017/02/03 11:01

maisumakun

総合スコア145121

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

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

kanade

2017/02/03 11:48

ご回答ありがとうございます! 直接変数に入れたほうがいいもの、そうではないものなどがわかりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問