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

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

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

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

Q&A

7回答

10922閲覧

C++のインスタンスについて

suran

総合スコア20

C++

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

3グッド

5クリップ

投稿2016/04/02 12:42

JAVA開発経験有のC++初心者です。

C++で仮にTestクラスのインスタンスを作成しメソッドを実行する場合下記の方法があるかと思います。

① Test inst;
inst.method();

② Test *inst = new Test();
inst -> method();

この違い、使い分けがよくわかっておりません。
②の方法はdeleteでメモリ解放をしなければならないため、
極力①を使った方がいいのではないかとも考えてしまうのですが・・。

raccy, nishiys, suittizihou👍を押しています

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

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

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

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

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

guest

回答7

0

この違い、使い分けがよくわかっておりません。

Chironian さんの説明にある通り、①の方法で定義した変数はブロックから抜けると解放されるため、残しておきたい場合は②の方法でヒープ上にインスタンスを確保します。

それ以外にも、①の方法ではスタック領域にインスタンスが確保されるため、あまり大きなサイズを扱えません。というのも、スタック上に大きな領域を確保しようとすると、場合によっては「スタックオーバーフロー」が発生する危険があるからです。そのため、インスタンスサイズが大きいクラスは、たとえ関数の中でしか使わなくても②の方法でインスタンスを確保することがあります。

ご質問でも指摘されているように②の方法では必ずdeleteする必要がありますが、C++では「スマートポインタ」という仕組みが一般的に使われており、deleteし忘れを防止することができます。
JavaやC#ではGC(ガベージコレクション)の仕組みによりnewしてもdeleteする必要はありません。C++もスマートポインタを使いこなすことで、Javaのようにdeleteせずとも自動でインスタンスの解放を制御できるようになります。

投稿2016/04/02 13:45

編集2016/04/03 22:01
catsforepaw

総合スコア5938

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

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

catsforepaw

2016/04/03 22:02

Java/C#での参照カウントについての記述は不正確だったので訂正しました。
guest

0

こんにちは。

②の方法はdeleteでメモリ解放をしなければならないため、

極力①を使った方がいいのではないかとも考えてしまうのですが・・。

はい、その通りです。

つまり、②を使う時があるのか?という疑問ですね。
①を使った場合、instの寿命は、instを宣言したブロック({}で囲まれた範囲)の中だけです。
ブロックが終了するとinstは強制的に開放されてしまいます。
例えば、関数の中で宣言した場合、関数からreturnすると開放されます。
それでは困る場合などに②を使います。

しかし、C++11/14で、ムーブ・セマンティクスやスマート・ポインタが強化されたため、どうしても②を直接使わなければ行けないケースは、実はもうないのではないか?とも感じています。(検証したわけではないので直感的な印象ですけど。)


【追記】
yohhoyさんのコメントを受けて本文を少し修正してます。
元は「②の必要はほぼない」と取れるニュアンスで書いてましたが、「②を直接使う必要がほぼない」へ変更しました。
②の考え方を理解する必要は今もこれからもあります。

投稿2016/04/02 13:07

編集2016/04/05 09:00
Chironian

総合スコア23272

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

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

yohhoy

2016/04/05 08:12

オブジェクトを配置するメモリを、①スタック領域から/②ヒープ領域から確保するのかという観点では、C++11/14時代でも②に相当する考え方は有効だと思います。 ただし、アプリケーション・コードとしては危険な生ポインタ Test* を使うのではなく、(回答中にもある)std::shared_ptr<Test> や std::unique_ptr<Test> のようなスマート・ポインタを使うべきという整理でしょうね。
Chironian

2016/04/05 08:45

yohhoyさん。 ②の考え方は有効(と言うか必要)ですね。舌足らずでした。フォローありがとうございます。 ところで、①はスタックから獲得するとは限らないです。他のクラスに含まれていて、そのクラスがヒープ領域で獲得されると①の形式でもヒープから獲得されます。グローバル変数やstatic変数だとデータセグメントから獲得されますので、スタックでもヒープでもなかったりします。
guest

0

たとえばこんなことやりたいときにポインタ使います。

C++

1Test* inst; 2if ( ファイルに出力 ) { 3 inst = new FileTest(); 4} else { 5 inst = new ConsoleTest(); 6} 7inst->method(); 8delete inst;

投稿2016/04/05 01:57

episteme

総合スコア16614

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

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

Chironian

2016/04/05 08:13

こんにちは。 ②の必要性の説明として良いですね。 しかし、method()が例外を投げるとinstがリークします。 そこで、ついでに最近の回避方法をコメントさせて下さい。 C++11以降では、このような場合、std::unique_ptr<>を使います。 (C++14以降ならnewさえ使わなくてよいようです。) {  std::unique_ptr<Test> inst;  if ( ファイルに出力 ) {   inst.reset(new FileTest());  } else {   inst.reset(new ConsoleTest());  }  inst->method(); } // ここを抜けるとinstが自動開放される。 ①は定義されたブロックを抜けるとTestのデストラクタが呼ばれます。 std::unique_ptr<>は、そのデストラクタが呼ばれる仕組みを利用し、デストラクタでポインタを自動開放しています。 そして、例外が発生してブロックから抜ける時もデストラクタはちゃんと呼ばれます。 従って、method()が例外を投げても、instを定義したブロックから出ると自動的にinstが開放されるので、例外安全です。
guest

0

まずC++11以降を利用する前提で話を勧めます。それ以前のC++を何らかの理由で強制される場合はtetolaさんやepistemeさんの回答を参照してください。

まずそのクラスの大きさはどの程度か、という問題があります。std::stringstd::vectorがいい例ですが、内部で動的確保をするなどしてクラス側ではポインタを持つような場合、クラスの大きさは一般に小さくなります。

というかクラスが巨大になるのって配列を持った構造体じゃないとまあ見ない気がする。

で、こういう小さなオブジェクトを動的確保すると、(処理系次第ですが)一般に速度が遅くなります。これは速度を追求するC++の精神に合致しません。したがってC++ではJavaやC#と違い、newする(スマートポインタの利用含む)ことはあまりありません。

クラスを関数から返すような場合でもコンパイラのNRVO/RVOという最適化が行われるのでnewする(スマートポインタの利用含む)メリットは皆無で、またその戻り値を一時変数に受ける場合もmoveセマンティックという、rvalue-referenceをある種のフラグとして使う技法により、deep copyが行われることがないコードが書けます。

C言語では動的配列を実現するために動的確保を多用しますが、C++においてはstd::vectorを使えばいいだけなので、この目的でnewないしスマートポインタを使うことはありません。

ではいつnewする(スマートポインタの利用含む)のかというと、例えば非同期処理をする時です。
【boost::asio buffers】 boost::asioでセッション管理にはshared_ptrが便利だ
非同期処理の場合、メモリーが意図せず開放されるのを避けるためにstd::shared_ptrが重宝されるようです。

なお余談ですが、std::vectorも要素数が小さいとやっぱり遅いので、stackメモリー制限が厳しくない環境では、要素数が十分小さく(800byte以下程度)要素数の上限がわかっている場合はstd::arrayを使うという高速化技法が存在します。
https://github.com/YSRKEN/KanColleSimulator_KAI/issues/64

とにかく動的確保はそれ自体重いので、stack overflowにならない範囲で動的確保を避けるのが、高速なプログラムが求められるC++では重要になります。なおstaticにデータを置くのは目に見えないデータ依存を作りやすくマルチスレッドするときに困るのでstaticの濫用は禁物です。
なお書いたプログラムがstack overflowになるかどうかは、コード解析ツールなどを使えば割りと簡単にわかるので、stackにデータを置くことをこわがらないでください。(Boostとか使ってるとたまにむちゃくちゃスタックを消費することがある)

ところでVSよ、なんでお前のstd::vectorはそんなにもメモリー管理が下手くそなんだ。事実上push_backするたびにメモリー再確保とデータコピーが発生するぞ・・・。毎回capacity管理をしろとでも?

投稿2016/04/12 00:02

yumetodo

総合スコア5850

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

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

0

C++でオブジェクトを作る場合は、次の4つが基本になるかと思っています。(Testクラスのオブジェクトを引数1を指定して作る場合です)

  1. (直接)データとして作る Test test(1);
  2. ポインタとして作る Test *test = new Test(1);
  3. unique_ptrとして作る std::unique_ptr<Test> test = std::make_unique<Test>(1);
  4. shared_ptrとして作る std::shared_ptr<Test> test = std::make_shared<Test>(1);
作り方データの場所データの寿命寿命延長方法
データスタック変数と同じ右辺値参照として返す
ポインタヒープdeleteされるまでdeleteされない限り永遠
unique_ptrヒープ変数と同じ他のunique_ptrにムーブする
shared_ptrヒープ変数と同じ他のshared_ptrにコピーする

通常のローカル変数はブロックがつきると寿命が尽きます。メンバー変数であればオブジェクトのデストラクタが呼ばれるまでが寿命です。グローバル変数やstaticローカル変数は永遠です。

まず、データもunique_ptrもshared_ptrもそのままでは変数が消えるタイミングで消えます。unique_ptrはポインタというよりもデータに近い動作をします。違いは、

  • 場所がスタックでは無くヒープになる。(どんなに大きなデータでもスタックオーバフローが発生する恐れが無いが、メモリ確保の処理がスタックより遅い場合があるため、利点でもあり欠点でもある)
  • ムーブコンストラクタが無いクラスのオブジェクトでも右辺値参照として返したり、他のunique_ptrにムーブすることができる。(そのかわりコピーはできない)
  • オブジェクトに関係なく、(ポインタの値を移動するだけなので)ムーブが極めて速い。

ということです。つまり、unique_ptrはポインタの利点のいくつかを追加してデータのように扱う方法と言った方が良いと思います。

shared_ptrはポインタとして作った時と似たようなことができます。変数と一緒に消されたくなければどこかにコピーしておけば、そのどこかが消えて無くならない限り、生き続けます。かといってそのどこかをきちんと管理しなければ、結局deleteし忘れと同じ結果を招くことでしょう。unique_ptrとの違いは、コピーにより複数の場所で同時に所有できると言うことです。※shared_ptrはコピーだけでなくムーブもできます。その場合は、unique_ptrのように寿命はムーブ先次第となります。

なお、例外等でdeleteし忘れによるメモリリークがあるからポインタはダメであると言うことではありません。例外が発生しうるのであれば、catchやfinallyでdeleteする処理を入れればいいだけです。それに、shared_ptrを使って、他の場所にもコピーを作った後に例外が発生した場合も、結局同じ事になります。

shared_ptrはGCの代わりになりません。weak_ptrでどれだけ工夫を凝らしたとしても循環参照を確実に回収できる保証があるわけではありません。これらはメモリ管理の煩わしさをある程度緩和するだけであって、GC並に何も考えなくても良いというわけでは無い事にもっと注意を払うべきです。

最後に、shared_ptrではなくポインタとして作る利点ですが、削除のタイミングを完全に把握できると言うことです。逆に言えば、きちんと把握していないとメモリリークや解放後にアクセスするなどのバグの元になります。それが良いのか悪いのかはケースバイケースだと思います。

C++は、GCという余計な処理が無い分、高速に動作することができますが、その代わり何でもコレ一つあれば大丈夫というものがありません。コードの内容から、その役割にあったオブジェクト生成方法を選ぶ必要があります。

投稿2016/04/11 12:50

raccy

総合スコア21735

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

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

0

①の場合、グローバル変数(クラスや関数の一番外にある変数)にしない限り、
関数などの { } を抜けた時点で解放されます。
グローバルの場合は途中で解放する事はできません。
(プログラム起動時に全てインスタンス化され、終わるまで解放されない)

②の場合、適切なタイミングで自前でdeleteするか、
なんらかの参照カウンターを用いて解放手段を用意する必要があります。

大量にメモリーを使う場合は、②を使わないと厳しくなります。
全部オンメモリで問題ない程度なら①をグローバルに持てば何も考える必要がありません。


なお、JavaやC#でのクラスはデフォルトで参照カウンターを用いていてますが、
C++はデフォルトに参照カウンターはなく、基本はコピーです。

例えば下記の場合、
myclass a;
myclass b;
a = b; aに bのクラス(sizeof(myclass)分)が丸ごとコピーされます(参照ではない)

myclass *a = new myclass(); (メモリ0x1000にmyclassが確保されたとする a=0x1000)
myclass *b = new myclass(); (メモリ0x2000にmyclassが確保されたとする b=0x2000)
a=b a=0x2000 になります。 (※アドレスコピーしているだけです。元々のa のクラスは解放されません)

なおC++ をやるなら、嫌でもメモリー管理が必要になりますので、
②になれて正しいメモリー解放手順等を理解された方が良いとは思います。

C++11で参照に関する強化は確かに行われているのですが、
実際の現場では、元々のコピーの概念を使用する機会の方が
まだ圧倒的に多いとは思います。

投稿2016/04/03 13:06

編集2016/04/03 13:50
tetola

総合スコア23

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

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

yumetodo

2016/04/05 07:29

moveできるクラスならnewする必要は皆無だし、できない場合でもスマートポインタに入れればいいだけ(ただしstd::share_ptrの参照カウンタの管理コストが重いのでunique_ptrで頑張る) つまりnewは書いてはいけない
tetola

2016/04/05 12:19 編集

スマートポインタ前提で書いてないのですけどね… 書いてはいけないとか言われると…真面目に回答してるのに感じ悪いですね…。 今後一生newを使わないというなら仕様から消したらいかがですか?newが存在する以上過去の名残で使用する機会はあると言いたいだけなのですがね…? あとすみませんが、質問者様はどこにも、スマートポインターに関して教えてくれと書いていなく、スタックの確保と、ヒープの確保の違いについて求めているのに、 なぜわざわざ難しくスマートポインターの方にもっていこうとされるのでしょうか? そもそものC++の基礎を理解せずに応用から理解させると理解が中途半端になってしまうと思われますが?(申し訳ありませんが、私はスマートポインターばかりに頼ってメモリーリークさせてる方々を散々見てきてますので…) 一般の理解って↓みたいな感じですが、こんな感じで気軽に飛びついて メモリーリークさせない自信はあるんでしょうかね…? http://seesaawiki.jp/w/boxer_programmer/d/C%2B%2B%A4%CB%A4%C4%A4%A4%A4%C6%A4%CE%A5%E1%A5%E2
yumetodo

2016/04/06 04:58

>メモリーリークさせない自信はあるんでしょうかね…? リンク先見ましたが、なんか古いC++だなぁ。 STLに入っているスマートポインタ(auto_ptr除く)使っている限りメモリーリークは起きないと思うんですが。確保している期間が長くならないようスコープに気をつけたりはしないといけませんがそれはメモリーリークとは別のお話ですし。 >あとすみませんが、質問者様はどこにも、スマートポインターに関して教えてくれと書いていなく、スタックの確保と、ヒープの確保の違いについて求めているのに 承知したうえで書いているつもりです。Javaはとりあえずnewする言語なので(偏見)、C++ではそうではない、ということを伝える必要があります(質問者も多分察しは付いているけど念の為に) 一般に大きなオブジェクトを返すのに動的確保が必要だと言われますが、moveのおかげで、move可能なクラスなら戻り値で返しても同じコストになりました。 で >嫌でもメモリー管理が必要になりますので は、私も同意なんですが、せっかくmoveのおかげでその場面の多くから逃げられるようになったんですから、いきなりそれをやるのはC++が嫌いになるだけじゃないかな、と思ってコメントしたんですが、なんでそれが伝わる文章を私は書けないんだろう・・・。すみません。
tetola

2016/04/06 10:38 編集

> リンク先見ましたが、なんか古いC++だなぁ。 c++に古いも新しいもないと思います。 昔から使われている言語ですのでスマートポインタすら導入されてない現場や開発プロジェクトは山のように現役で存在します。ちなみにあなたが古いとおっしゃるソースもブログは2016年3月のものです。つまり現役のソースコードです。 もしそういった開発現場でもnewするなと言えますでしょうか? どれも同じc++です。それだけです。
84taka0310

2016/04/11 03:23

よこから失礼いたします。 うちも「スマートポインタすら導入されていない現場」ですので、tetolaさんのご意見に賛同します。 C++に古いも新しいもないかな、と。 というより、開発現場によって適切に運用されているはずなので、そこのルールに従うことが適当な回答と思います。 ヒープ領域の考え方は大切ですが、常にnewをすれば良いわけでもない(速度面の考慮で)ですし、記述行数が増えれば増える程処理が遅くなることを考慮すればstaticも有りかなと思います。
tetola

2016/04/11 10:49

賛同ありがとうございます。 c++の記述は歴史が長い分多種多様なので、一つだけが正解ではないですからね。臨機応変に対応できるようになるべきだと思います。話がそれて申し訳ない。
yumetodo

2016/04/11 23:25 編集

様々な実装のC++が現場で使われているのは百も承知です。それを踏まえて、初学者にいきなりそれを伝えるのはどうなんだ、という話です。 C++で食べていくというのでなければ、どのC++バージョン、実装を使うかは自由です。そんな時にわざわざ古いC++を使うことは無いだろうに、ということが言いたかった。 なお >C++に古いも新しいもないかな、と。 には明確に反対します。C++11以降とそれより前では明確にコーディングのスタイルが違います。同じ言語と捉えることは誤解を誘発します。私にはCとC++は同じだ、と言っているくらいに違和感を覚えます(あくまで比喩です)
tetola

2016/04/12 03:50 編集

むしろ初学者にスマートポインタ進める方が違和感ありますが? あとc++はcを内包してますので、 極端に言えばcもc++の一部ですので同じです。 あと別に、私はスマートポインタとか否定してるわけではないんですけど、私の意見否定して正解は1つだみたいな言い方されたから反論してるってわかりますかね?
yumetodo

2016/04/13 10:28

>むしろ初学者にスマートポインタ進める方が違和感ありますが? その通りです。なので私は最初に >moveできるクラスならnewする必要は皆無だし と書きました。newそのまま書くにしろ、スマートポインタを使うにしろ初心者向きではありませんし。 >私の意見否定して正解は1つだみたいな言い方されたから反論してるってわかりますかね? 現場ではC++11が使えないのかもしれないけどC++11を考慮しないのもどうなのかな、と言いたかっただけなのですが、どんどん話をそらしてしまう結果となり、申し訳ないです
tetola

2016/04/13 16:02 編集

スマートポインタだろうが、ムーブだろうが、どっちでもいいんですが、 話ややこしくしてると思いませんか? そもそもの質問のどこかに、std::move とか書かれてますか? ① Test inst; inst.method(); ② Test *inst = new Test(); inst -> method(); そもそもの質問は、この違いですよね?別にC++11使うなとは言ってませんが、 それ前提ってのはどうなんですか? moveがあれば new いらないなら、 そもそも new の存在は不要で、new って何? delete 必要ですよね?という質問がでないはずなのですけど…。 私は new について聞かれたので答えているのであって、 この質問は、moveについて尋ねている質問ではないですよね? よーするに何が言いたいかっていうと、 私の回答に move がどうたらとマイナス評価して反論してくる意味が そもそも解らないんですよ。 私は質問に対して 値のコピー と new の説明をしているだけなのですから。
guest

0

②の方法はdeleteでメモリ解放をしなければならないため、

極力①を使った方がいいのではないかとも考えてしまうのですが・・。

私もそう思います。
ただ、どうしても②を使わざるを得ない局面もあります。
単純に思いつくのは巨大な配列や構造体を使う場合です。

①と②では変数を確保するメモリ領域が違います。①はスタック、②はヒープに確保します。確保できるメモリ容量はスタックに比べてヒープの方が多いので(というかスタックはレジスタの退避にも使われるので大量のメモリ確保をすべきではない)、画像の読み込みなどに使用するメモリはヒープから確保するようになっている場合が多いと思います。

投稿2016/04/02 13:55

KoichiSugiyama

総合スコア3041

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問