コンストラクタの初期化子newした変数の後始末はどうすべきなのか? – プログラミング – Home

コンストラクタの初期化子newした変数...
 
通知
すべてクリア

[解決済] コンストラクタの初期化子newした変数の後始末はどうすべきなのか?


こんちゃん
 こんちゃん
(@こんちゃん)
ゲスト
結合: 12年前
投稿: 3
Topic starter  

大きな配列を定数で初期化する方法としてこのよなコードをネットで拝見しましたが
このコードに疑問があります。
Square() : table(new int[1000])
メンバ変数table を初期化リストで、new 演算子を使い動的にを生成していますが、この
後始末をdelete 演算子を使って明示的に開放する必要はありませんか。
具体的に書くと、これは要らないのでしょうか?
Square()::~Square()
{
delete []table;
}

// -- square.h --
#pragma once
class Square
{
int *table;
public:
Square() : table(new int[1000])
{
for (int i = 0; i < 1000; ++i) table[i] = i * i;
}
int operator()(const int i) const { return table[i]; }
};

extern const Square square;

// -- global.cpp --
#include square.h
#include <iostream>

const Square square;

using namespace std;

int main()
{
for(int i=0; i<1000; ++i)
{
cout << square(i) << endl;
}

return 0;
}


引用未解決
トピックタグ
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

delete[] 必要です。
ただこのコードの場合 ~Square が実施されるのはプログラム終了時で、
Square はメモリ以外のリソースを取っていないため、無くても実害ない、です。
(ファイルとか、ソケットとか、そういう非メモリリソースを取っていると必須)
たまたま<strong>この例では</strong>実害ない
だけで、あってしかるべき。そのコードは説明のための手抜きをしていると推測。

手抜きは他にもあります。一歩進んでみますか?

メンバー配列が2個以上になったときなどに、単純に
Square::Square() : table1(new int [N]), table2(new int [M]) ....
としてしまうといろいろまずい。
table2 の new の際に失敗すると table1 を delete[] する術がない
table1 の new の際に失敗すると table2 が不定値で delete[] に渡すと失敗する
(グローバル変数でなくした場合)

function-try-block を調べてみるべし。
スマートポインタを調べてみるべし。


返信引用
こんちゃん
 こんちゃん
(@こんちゃん)
ゲスト
結合: 12年前
投稿: 3
Topic starter  

tetrapod さんありがとうございます。
省略してあった理由が分かりました。
コンストラクタでの例外処理は絶対NGと思い込んでいたので
function-try-blockは大変参考になりました。
スマートポインタ は知っていましたが、当該コードのような配列には使えないですよね?


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

デストラクタで例外を投げるのは絶対禁止(捕捉できずに死ぬだけ)だけど
コンストラクタで例外を投げるのは普通のことです。
いや、むしろ「適切」である場合のほうが多い。
「禁じ手」本が間違っているだけ。

提示例にはツッコミどころがいっぱいあるんだけど、あえて目をつぶるとして・・・
new の例外に対して安全にするという1点だけ対処するとしたら、方策は
・ 生ポインタ+function-try-block とか
・ boost::scoped_array<int> とか
・ std::vector<int> とか
・ std::tr1::array<int,N> とか
いくつも思いつくわけで、お好きな方式でどうぞ。


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 15年前
投稿: 64
 

>・ 生ポインタ+function-try-block とか
>・ boost::scoped_array<int> とか
>・ std::vector<int> とか
>・ std::tr1::array<int,N> とか

C++11 (VC10,11) なら unique_ptr もオススメ

#include <cstdio>
#include <memory>

class item {
public:
item() { puts(ctor); }
virtual ~item() { puts(dtor); }
};

class obj {
public:
obj(int n) : items(new item[n]) {}
private:
std::unique_ptr<item[]> items;
};

int main() {
obj x(3);
// ちゃんと3回 ~item が動く
}


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

元ネタは
http://d.hatena.ne.jp/shogicraft/20120217/1329497890

このページで論じているのは定数として使いたい巨大配列を実行時初期化したいが
オーバーヘッドはどうなるか・・・ですな。

プログラムの初期化のタイミングで作られ、終了までずっと使う定数テーブルだから
・定数であるがゆえにコピー代入も考える必要ないし
・失敗するならプログラムは異常終了でいいので例外捕捉も不要
・純粋にテーブルだからメモリ以外のリソースは絶対に使用しないし
・テーブルの破棄はプログラム終了時点でいいのでデストラクタ明示不要
ってことで最初のサンプルになる、ってことで。

コンストラクションのオーバーヘッド
使う際のオーバーヘッド
しか考察対象にしていない、ということで最初の提示コードになるわけですな。
元記事ライターはあえて記事の焦点を絞っているわけですよ。
デストラクタいらないの?例外安全いらないの?って突っ込みは野暮ってもんだ。


返信引用
こんちゃん
 こんちゃん
(@こんちゃん)
ゲスト
結合: 12年前
投稿: 3
Topic starter  

みなさんどうもありがとう。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました