私や他の多くの人は、スマートポインターを使用して、RAIIなどを使用して、C++での安全でないメモリ操作をまとめることで大きな成功を収めていると思います。ただし、デストラクタ、クラス、演算子のオーバーロードなどがある場合は、ラッピングメモリ管理を実装する方が簡単です。
生のC99で書いている人にとって、安全なメモリ管理を支援するためにどこを指すことができますか(しゃれは意図されていません)?
ありがとう。
質問は少し古いですが、私は自分の スマートポインタライブラリ for GNUコンパイラ(GCC、Clang、ICC、MinGW、 ...)。
この実装は、クリーンアップ変数属性a GNU拡張子に依存して、スコープ外に出たときにメモリを自動的に解放するため、notISO C99、ただしGNU拡張子を持つC99。
例:
simple1.c:
#include <stdio.h>
#include <csptr/smart_ptr.h>
int main(void) {
smart int *some_int = unique_ptr(int, 1);
printf("%p = %d\n", some_int, *some_int);
// some_int is destroyed here
return 0;
}
コンパイルとValgrindセッション:
$ gcc -std=gnu99 -o simple1 simple1.c -lcsptr
$ valgrind ./simple1
==3407== Memcheck, a memory error detector
==3407== Copyright (C) 2002-2013, and GNU GPL\'d, by Julian Seward et al.
==3407== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==3407== Command: ./simple1
==3407==
0x53db068 = 1
==3407==
==3407== HEAP SUMMARY:
==3407== in use at exit: 0 bytes in 0 blocks
==3407== total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==3407==
==3407== All heap blocks were freed -- no leaks are possible
==3407==
==3407== For counts of detected and suppressed errors, rerun with: -v
==3407== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
使用法をバックアップするための言語構文がないため、rawCでスマートポインターを処理することは困難です。私が見たほとんどの試みは実際には機能しません。オブジェクトがスコープを離れるときにデストラクタが実行されるという利点がないためです。これがスマートポインタを機能させる理由です。
これについて本当に心配している場合は、 ガベージコレクター を直接使用し、スマートポインターの要件を完全にバイパスすることを検討することをお勧めします。
検討したいもう1つのアプローチは、 Apacheが使用する であるプールメモリアプローチです。これは、リクエストまたはその他の短期間のオブジェクトに関連付けられている動的なメモリ使用量がある場合に非常にうまく機能します。リクエスト構造にプールを作成し、常にプールからメモリを割り当て、リクエストの処理が完了したらプールを解放するようにすることができます。少し使ってみると、それほどパワフルに聞こえません。 RAIIとほぼ同じくらいいいです。
必要な構文が提供されていないため、Cでスマートポインターを実行することはできませんが、練習すればリークを回避できます。リソースリリースコードは、割り当てた直後に記述してください。したがって、malloc
を書き込むときはいつでも、対応するfree
をクリーンアップセクションにすぐに書き込む必要があります。
Cでは、「GOTOクリーンアップ」パターンがよく見られます。
int foo()
{
int *resource = malloc(1000);
int retVal = 0;
//...
if (time_to_exit())
{
retVal = 123;
goto cleanup;
}
cleanup:
free(resource);
return retVal;
}
Cでは、ものを割り当てる多くのコンテキストも使用します。同じルールをそれに適用することもできます。
int initializeStuff(Stuff *stuff)
{
stuff->resource = malloc(sizeof(Resource));
if (!stuff->resource)
{
return -1; ///< Fail.
}
return 0; ///< Success.
}
void cleanupStuff(Stuff *stuff)
{
free(stuff->resource);
}
これは、オブジェクトのコンストラクタとデストラクタに類似しています。割り当てられたリソースを他のオブジェクトに渡さない限り、リークしたり、ポインタがぶら下がったりすることはありません。
割り当てを追跡し、リークしているブロックatexit
を書き込むカスタムアロケータを作成することは難しくありません。
割り当てられたリソースへのポインターを提供する必要がある場合は、そのラッパーコンテキストを作成でき、各オブジェクトはリソースの代わりにラッパーコンテキストを所有します。これらのラッパーは、リソースとカウンターオブジェクトを共有します。カウンターオブジェクトは、使用状況を追跡し、誰も使用しない場合にオブジェクトを解放します。これがC++ 11のshared_ptr
およびweak_ptr
動作します。ここに詳しく書かれています: weak_ptrはどのように機能しますか?
中括弧の代わりに使用するマクロ(BEGINやENDなど)を定義して、スコープを終了するリソースの自動破棄をトリガーできます。これには、そのようなすべてのリソースが、オブジェクトのデストラクタへのポインタも含むスマートポインタによってポイントされている必要があります。私の実装では、スマートポインターのスタックをヒープメモリに保持し、スコープの入り口でスタックポインターを記憶し、スコープの出口で記憶されたスタックポインターの上のすべてのリソースのデストラクタを呼び出します(ENDまたはリターンのマクロ置換)。これは、setjmp/longjmp例外メカニズムが使用されている場合でもうまく機能し、catch-blockと例外がスローされたスコープの間のすべての中間スコープもクリーンアップします。実装については、 https://github.com/psevon/exceptions-and-raii-in-c.git を参照してください。
Win32でコーディングしている場合は、 構造化例外処理 を使用して同様のことを実行できる可能性があります。あなたはこのようなことをすることができます:
foo() {
myType pFoo = 0;
__try
{
pFoo = malloc(sizeof myType);
// do some stuff
}
__finally
{
free pFoo;
}
}
RAIIほど簡単ではありませんが、すべてのクリーンアップコードを一か所に集めて、確実に実行することができます。
Sometimes i use this approach and it seems good :)
Object *construct(type arg, ...){
Object *__local = malloc(sizeof(Object));
if(!__local)
return NULL;
__local->prop_a = arg;
/* blah blah */
} // constructor
void destruct(Object *__this){
if(__this->prop_a)free(this->prop_a);
if(__this->prop_b)free(this->prop_b);
} // destructor
Object *o = __construct(200);
if(o != NULL)
;;
// use
destruct(o);
/*
done !
*/