ページ 1 / 1
これだと不都合でしょうか
Posted: 2007年8月12日(日) 12:05
by まだまだ
こんにちは
下記のような書き方はだめでしょうか。
class Aのメンバーとして short *st = NULL; があるとします。
そしてAのconstructorに以下のような文を発行します
st = new short(100);
~A() には
delete[/url] st;
を発行します。
そうしたら下記のようなやり方はだめしょうか。
short *foo()
{
A a;
return a.st;
}
foo()が終わり時点で、aが消滅され、aのdeconstructorで
stの本体を解放されてしまいます?
もしほんとうにだめなら、どうやって回避できるのでようか。
ご教授お願い致します。
Re:これだと不都合でしょうか
Posted: 2007年8月13日(月) 00:02
by たかぎ
> class Aのメンバーとして short *st = NULL; があるとします。
NULLで初期化していますが、どういう意味でしょうか?
静的データメンバでしょうか?
> そしてAのconstructorに以下のような文を発行します
> st = new short(100);
> ~A() には
> delete[/url] st;
> を発行します。
発行というのがよくわかりませんが、new short(100)は配列用のものではなく、short型のオブジェクトを1個生成し、その値を100にするという意味です。ですから、delete[/url]で解放するのは間違っています。
もう少し状況を明確にしてください。
Re:これだと不都合でしょうか
Posted: 2007年8月13日(月) 12:46
by まだまだ
たかぎ様
> short *s t= NULL;
間違っています。
昔の名残で、short *st; であるべき。
”発行する”という言い方は、書いて実行させるという意味で、
不明確でごめんなさい。
> new short(100);
これも大きな間違いで、ご指摘のとおり
new short[100];
であるべきです。
[このような間違いは実は以前自分書いたC++のソースにありました]
再度お願い致します。
Re:これだと不都合でしょうか
Posted: 2007年8月13日(月) 13:20
by たかぎ
それでは本題に戻って...
> もしほんとうにだめなら、どうやって回避できるのでようか。
本当にダメです。理由はお分かりの通りです。
回避の方法ですが、foo 関数の処理が本当にこれだけなのか、それとも何らかの副作用を伴うのかによって、最適な方法が異なります。
まず、配列形式の new は、同じクラスや関数の中など、閉じた範囲で使うのであれば構いませんが、そうではなく、他とのインタフェースに使うのはやめた方が無難です。今回のように、配列形式の new で割付けたオブジェクトへのポインタを返した場合、foo 関数の呼び出し側では、その解放方法を間違う可能性が大だからです。
そこで、配列形式の new の代わりに、vector クラステンプレートを使うことをお勧めします。次のような感じです。
class A
{
std::vector<short> st;
public:
A() : st(300, 0) {}
const std::vector<short>& get_st() const { return st; }
};
std::vector<short> foo()
{
A a;
return a.get_st();
}
この方法だと、foo 関数から返る際に vector がコピーされるため、若干の効率の悪さはありますが、多くの問題を回避できますし、使い勝手もよいと思います。
しかし、foo 関数内では、実はもっと他の処理を行い、結果として副作用を伴うのであれば、先ほどの方法は強い例外安全を保障できなくなります。そこで、
class A
{
std::auto_ptr<std::vector<short> > st;
pulic:
A() : st(new std::vector<short>(300, 0)) {}
A(const A& other) : st(new std::vector<short>(*other.st)) {}
A& operator=(const A& other)
{
A t(other);
swap(t);
return *this;
}
void swap(A& other)
{
std::swap(st, other.st);
}
std::auto_ptr<std::vector<short> > get_st() { return st; }
}
std::auto_ptr<std::vector<short> > foo()
{
A a;
return a.get_st();
}
とすることで、問題を回避できます。
なお、先ほどのものとは、get_st メンバ関数のセマンティクスが異なりますので注意してください。
Re:これだと不都合でしょうか
Posted: 2007年8月13日(月) 17:33
by まだまだ
たかぎさま
ご返答ありがとうございます
詳しい例大変助かりました。
正直に言って例の理解に少々時間かかりました。
C(C++)では文字列や配列を渡したりすることは日常茶飯事のようなもので、
こんな大掛かりの設計を必要するのは知りませんでした。
私の理解正しいかどうか、ちょっと確認させていただきたいですが、
'='を代入として定義しておいて、そして代入する時に、swapで相手class(object)のメンバーに
配列の内容を丸ごと複製することはこのやり方のみそでしょうか。
そうであれば、例えば画像データ(長い配列)を渡すのに毎回毎回コピーすることは
時間的に許せない側面もあります、、、
私の理解が正しくないかもしれませんが。
またご教授お願いできればと思います。
Re:これだと不都合でしょうか
Posted: 2007年8月15日(水) 15:02
by たかぎ
> '='を代入として定義しておいて、そして代入する時に、swapで相手class(object)のメンバーに
> 配列の内容を丸ごと複製することはこのやり方のみそでしょうか。
いいえ。重要なのは、std::auto_ptr を用いてデータを管理する部分です。
ただし、std::auto_ptr を使えば、デフォルトのコピーコンストラクタやコピー代入演算子が使えなくなるため、どうしても上のように定義しなおす必要が出てきます。あるいは、コピーできないように、コピーコンストラクタやコピー代入演算子を隠蔽してしまうかです。
なお、swap は st(すなわち std::auto_ptr )をコピーするもので、ここでは複製は起きません。
複製しているのは、その直前の A t(other); の部分です。
> そうであれば、例えば画像データ(長い配列)を渡すのに毎回毎回コピーすることは
> 時間的に許せない側面もあります、、、
コピーコンストラクタやコピー代入演算子は、内部的に管理しているデータを丸ごとコピーするのが基本です。しかし、今回の場合は、foo 関数から返しているのは std::auto_ptr であり、ポインタを1個返すのと、それほど効率が変わるわけではありません。
お礼
Posted: 2007年8月15日(水) 17:40
by まだまだ
たかぎ様
丁寧なご解説よくわかりました。
ほんとうにいい勉強になりました。
お蔭様でC++の面白みを少しずつ感じるようになりました。
ほんとうにありがとうございます