質問するログイン新規登録
C++

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

意見交換

クローズ

5回答

1941閲覧

グローバルで確保したメモリの解放のタイミングはどこになりますか?

airi_pumpkin

総合スコア30

C++

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

0グッド

2クリップ

投稿2024/01/12 15:04

0

2

知りたいこと

グローバルで確保されたメモリが解放されるタイミング

背景、状況

以前,業務でグローバルに定義したクラスをmain内で解放する実装をしたのですが,プロジェクトリーダーに「globalで確保したメモリをmain内で解放するのはメモリ管理上バグりそうで避けたい」と言われました.
個人的にはプログラムが終了する直前で解放してるから問題ないのでは?と思うのですが,私が配慮できていない問題があるのでしょうか?

やはり,C++でメモリを扱う場合はスマートポインタを使うのが無難ですか?

#include <iostream> using namespace std; class TEST { public: string Name; TEST(string name) { Name = name; cout << Name << " construct" << endl; } ~TEST() { cout << Name << " destruct" << endl; } }; TEST *AA = new TEST("AA"); int main(void) { delete AA; return 0; } ~~実行結果~~ AA construct AA destruct

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

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

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

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

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

回答5

#1

退会済みユーザー

退会済みユーザー

総合スコア0

投稿2024/01/12 17:28

グローバルに存在する変数はプロセスが存在する限り存在し、どこからでも参照される可能性があります。

例えば、以下を実行するとうちの環境ではセグメンテーションフォルトになりました。

bash

1cat >hoge.cpp <<EOF 2#include <iostream> 3 4using namespace std; 5class TEST { 6public: 7 string Name; 8 TEST(string name) { 9 Name = name; 10 cout << Name << " construct" << endl; 11 } 12 ~TEST() { 13 cout << Name << " destruct" << endl; 14 } 15}; 16TEST *AA = new TEST("AA"); 17 18// 追加 19void hoge() { 20 cout << AA->Name << endl; 21} 22 23int main(void) { 24 atexit(hoge); // 追加 25#ifdef DELETE 26 delete AA; 27#endif 28 return 0; 29} 30EOF 31g++ -g hoge.cpp -o hoge 32./hoge 33g++ -g -DDELETE hoge.cpp -o hoge 34./hoge

結果

text

1AA construct 2AA 3AA construct 4AA destruct 5Segmentation fault (core dumped)

他にもシグナルや割り込みやスレッドなどがグローバル変数にアクセスする可能性があります。特にdelete後にnullクリアしていないポインタなどは未定義動作の元になります。

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

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

#2

SaitoAtsushi

総合スコア5740

投稿2024/01/13 04:58

main 内で解体するかどうかの問題ではなく、根本的に順序が適切かどうかの問題です。 順序の適切さを守るための指針としてスマートポインタを活用する方向で一貫させるというプロジェクトルールを定めるのであればそれは納得できるものです。 (それだけで適切さが保証されるわけではありませんが。)

質問で提示されたコードのように使うのであればそれ自体は問題ありません。 ただ、他のグローバル変数 (静的記憶域期間をもつオブジェクト) を参照して (または参照されて) いるようなことがあると適切な順序で構築・解体する必要があるということはあり得ます。

グローバル変数の構築・解体の順序のルールは色々とややこしくて、未規定な部分や最適化を許している部分もあるので複雑な依存関係があるときは正しく順序を管理するのが困難です。 グローバル変数の構築順序に関するルールを言えますか?

解体の順序は構築の逆順なので構築で問題が起きなければ解体の順序が問題になるということはあまり起こりません。 main 内で delete するという方式だとその順序を乱すことになるのでスマートポインタを使ったほうが問題が起こり難くはなると思います。

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

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

#3

a_saitoh

総合スコア702

投稿2024/01/16 14:11

いつ解放されるかと言われたら「deleteの時点で解放される」でしょうけど・・・・。
「deleteの後はexit呼び出ししかしない」場合にはそのdeleteは不要(わざわざ書いても利益がない)のではないでしょうか。

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

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

#4

fana

総合スコア12247

投稿2024/01/17 02:13

編集2024/01/17 02:19

プログラムが終了する直前で解放してるから問題ないのでは?

「直前」とはいつなのか?
delete 時点からそのプログラム(プロセス?)が終了するまでの間にも期間があってそこで何かが実施され得る.
故に

グローバルに定義したクラス

この時点で,他の方が述べられているような事柄が「あり得る/考えられる」.
(→本当にそういう事柄が起きるのか否かはそのプログラム次第なので,全てを知っていれば「今は これで大丈夫」と言うことも可能ではある.)

グローバルであることが「あり得る/考えられる」事柄の要因である場合には,

スマートポインタを使うのが無難ですか?

みたいなことよりも前に,まずは「グローバル」を見直すことができないのか? を検討するのが良いと思う.

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

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

#5

退会済みユーザー

退会済みユーザー

総合スコア0

投稿2024/01/17 02:50

main関数の外側でも何気なく使用しちゃっているstd::coutみたいなのもグローバル変数なので、グローバル変数自体を否定するつもりはありません。ただ、原則としてグローバル変数の概念的な生存期間はプロセス開始時から終了時までずっと…というのが通例で、そうならないものはあまりグローバルにはしない方がいいと思います。そういう意味でスマートポインタは生成と消滅が頻繁に発生し、煩雑な管理が必要になるものに使用するのではないかと思うので、グローバル変数には向いてないかなぁと個人的には思います。

ここからは完全に個人的な意見ですが、そういう意味でグローバル変数の初期化/インスタンス生成に構築順序はどうしても必要なのですが(それでもなるべく単体で独立した形が望ましい)、破棄は不要だと思っています。例えばstd::outは最後の瞬間まで使えてほしいから。もちろんポインタでない場合、ちゃんとデストラクタとか呼ばれるんですけどね(なのでグローバル変数にするならsingletonにして必要になったときにインスタンス生成し、破棄しないというのが個人的には安心です)。明示的な破棄がないと正しい動作をしないようなオブジェクトはそもそもグローバル変数にすべきじゃない、というくらいの気持ちです。

最後になりましたが、静的変数の初期化はメモリ転送でガバっとしたり、main前にコンストラクタが順番に呼ばれたりして初期化されます。逆にデストラクタはmain終了後にこれまた順番に呼ばれます。gccだとこの辺に少し書いてあります。

https://gcc.gnu.org/onlinedocs/gccint/Initialization.html

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

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

最新の回答から1ヶ月経過したため この意見交換はクローズされました

意見をやりとりしたい話題がある場合は質問してみましょう!

質問する

関連した質問