52

私と他の多くの人は、RAII などを使用して、C++ で安全でないメモリ操作をラップするためにスマート ポインターを使用して大きな成功を収めたと思います。ただし、デストラクタ、クラス、演算子のオーバーロードなどがある場合は、メモリ管理をラップする方が簡単に実装できます。

生のC99で書いている人にとって、安全なメモリ管理を支援するためにどこを指摘できますか?

ありがとう。

4

9 に答える 9

27

質問は少し古いですが、時間をかけてGNU コンパイラ (GCC、Clang、ICC、MinGW など) 用のスマート ポインター ライブラリにリンクすると思いました。

この実装は、クリーンアップ変数属性 (GNU 拡張機能) に依存して、スコープ外に出るときにメモリを自動的に解放します。そのため、ISO 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)
于 2015-01-10T10:22:52.170 に答える
13

生の C でスマート ポインターを処理するのは困難です。これは、使用法をバックアップする言語構文がないためです。オブジェクトがスコープを離れたときにデストラクタを実行するという利点がないため、私が見た試みのほとんどは実際には機能しません。これが実際にスマートポインタを機能させるものです。

これについて本当に心配している場合は、ガベージ コレクターを直接使用し、スマート ポインターの要件を完全にバイパスすることを検討してください。

于 2009-04-28T21:11:15.667 に答える
8

考慮すべきもう 1 つのアプローチは、 Apache が使用するプールされたメモリ アプローチです。これは、リクエストまたはその他の存続期間の短いオブジェクトに関連付けられた動的メモリ使用量がある場合に非常にうまく機能します。リクエスト構造にプールを作成し、常にプールからメモリを割り当て、リクエストの処理が完了したらプールを解放するようにすることができます。少し使ってみると、それほどパワフルには聞こえません。RAIIとほぼ同じくらい素晴らしいです。

于 2009-04-28T21:27:10.310 に答える
5

C では必要な構文が提供されないためスマート ポインターを作成できませんが、練習すればリークを回避できます。リソース解放コードは、割り当てた直後に記述してください。したがってmalloc、 を記述するときはいつでも、対応するものfreeをすぐにクリーンアップ セクションに記述する必要があります。

CI では、「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はどのように機能しますか?

于 2012-08-11T10:00:03.340 に答える
3

ここでは、 splintGimpel PC-Lintなどの静的コード分析ツールが役立ちます。自動の「継続的インテグレーション」スタイルのビルド サーバーに配線することで、これらを適度に「予防」することもできます。(あなたはそれらのうちの1つを持っていますよね?:にやにや:)

このテーマには他の (より高価な) バリエーションもあります...

于 2009-04-28T21:16:59.320 に答える
1

Win32 でコーディングしている場合は、構造化された例外処理を使用して同様のことを達成できる場合があります。次のようなことができます。

foo() {
    myType pFoo = 0;
    __try
    {
        pFoo = malloc(sizeof myType);
        // do some stuff
    }
    __finally
    {
        free pFoo;
    }
}

RAII ほど簡単ではありませんが、すべてのクリーンアップ コードを 1 か所に集めて、確実に実行することができます。

于 2009-04-28T21:22:11.477 に答える
-2
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 !
*/
于 2015-08-26T18:03:14.967 に答える