1. 程式人生 > >c語言智能指針 附完整示例代碼

c語言智能指針 附完整示例代碼

HERE var 大坑 lan app pat pil 管理 clean

是的,你沒有看錯,

不是c++不是c#,

就是你認識的那個c語言。

在很長一段時間裏,c的內存管理問題,

層出不窮,不是編寫的時候特別費勁繁瑣,

就是碰到內存泄漏排查的各種困難,

特別在多線程環境下,就難上加難了,

諸如此類的老大難問題。

c++用它的RAII機制妥妥影響了一代程序員。

RAII大概介紹下,就不做科普,

有需要的同學,百度一下了解細節。

什麽是RAII

資源獲取即初始化 (Resource Acquisition Is Initialization, RAII),RAII是一種資源管理機制,資源的有效期與持有資源的對象生命期嚴格綁定,即由對象的構造函數完成資源的分配,由析構函數完成資源的釋放,總結一句話就是 用對象來管理資源

RAII實現原理

當一個對象離開作用域的時候就會被釋放,會調用這個對象類的析構函數,這都是自動管理的,不需要我們手動調用。所以我們可以把資源封裝到類的內部,當需要用資源的時候初始化對象即可,當對象被釋放的時候資源也會被釋放

當你寫了多年c代碼,你是多麽渴望有這麽一個東西可以給到你。

眾望所歸,終於gcc編譯器開了個小竈,留了一個後門造福c程序員。

詳情見:

https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html

主要看這個:

cleanup (cleanup_function)
The cleanup attribute runs a function when the variable goes out of scope. This attribute can only be applied to auto function scope variables; it may not be applied to parameters or variables with static storage duration. The function must take one parameter, a pointer to a type compatible with the variable. The return value of the function (if any) is ignored.

If -fexceptions is enabled, then cleanup_function is run during the stack unwinding that happens during the processing of the exception. Note that the cleanup attribute does not allow the exception to be caught, only to perform an action. It is undefined what happens if cleanup_function does not return normally.

這個cleanup機制,用起來,妥妥就是一個c的析構函數了。

沒有必要造輪子,輪子已經造好了。

libcsptr提供了常用智能指針的封裝,

unique_ptr, shared_ptr ,絕對是夠用了。

項目地址:

https://github.com/Snaipe/libcsptr

花了點小時間編寫示例代碼,造福大家。

順手解決vs的編譯問題。

另外說一下,vs不是gcc,沒有cleanup 可以實現這個功能。

不過可以通過安裝llvm在vs裏選擇llvm編譯進行編譯。

到https://llvm.org/releases/download.html

下載後安裝,再啟動vs就可以看到編譯器選項了。

貼個圖上來,可參照一下。

技術分享圖片

選好之後,就可以玩起來一些clang特性了。

完整示例代碼:

#include <stdio.h>
#include "csptr_smart_ptr.h"

struct BufferBody {
    char *buffer;
    size_t size;
};

static void callback_dtor(void *ptr, void *meta) {
    (void) meta;
    struct BufferBody *ctx = ptr;
    if (ctx->buffer != NULL)
        free(ctx->buffer);
}

struct BufferBody *write_buffer(const char *bufbody, size_t init_body_len) {
    smart struct BufferBody *ctx = shared_ptr(struct BufferBody, { 0 }, callback_dtor);
    if (!ctx) // failure to allocate
        return NULL; // nothing happens, destructor is not called

    if (ctx->buffer == NULL) {
        ctx->buffer = malloc(init_body_len);
        if (ctx->buffer != NULL)
            ctx->size = init_body_len;
    } else {
        if (ctx->size < init_body_len) {
            ctx->buffer = realloc(ctx->buffer, init_body_len);
            if (ctx->buffer != NULL)
                ctx->size = init_body_len;
        }
    }
    size_t buflen = strlen(bufbody);
    if (ctx->size > buflen)
        memcpy(ctx->buffer, bufbody, buflen);
    return sref(ctx); // a new reference on bufCtx is returned, it does not get destoyed
}

void do_something(size_t init_body_len) {
    smart struct BufferBody *ctx = write_buffer("hello smart ptr.", init_body_len);
    printf("%s \n", ctx->buffer);
    // ctx is destroyed here
}

int main(void) {
    printf("Smart pointers for the (GNU) C\n");
    printf("blog: http://cpuimage.cnblogs.com/\n");
    printf("tips: u can use llvm to compile in visual studio");
    printf("download llvm: http://releases.llvm.org/download.html");
    // some_int is an unique_ptr to an int with a value of 1.
    smart int *some_int = unique_ptr(int, 1);
    printf("%p = %d\n", some_int, *some_int);
    size_t init_body_len = 4096;
    do_something(init_body_len);
    // some_int is destroyed here
    return 0;
}

非常簡單,代碼嚴謹性不深究,大家看下示例的具體用法就可以了。

我都覺得簡單得寫註釋都有點多余。

由於原項目文件挺多的,編譯挺麻煩的。

就操刀簡單修改了一下,

主要是將代碼合為一個文件csptr_smart_ptr.h,附示例代碼,幹凈便攜。

對應項目地址:

https://github.com/cpuimage/libcsptr

只能說,有了它,你可以省下不少c內存管理心了。

當然會有很多人質疑說,會不會有大坑,

也許會有,也許沒有,但是c智能指針的確可以有。

我比較相信事實,當然事實就是編譯器提供了一個路子給你,

然而有些人確實可能會說,不相信編譯器,

嗯,對的,我也不信。

但是,毫無疑問,大家雖然不信但是都在用。

嘴上那樣說,身體還是很誠實的。

以上,權當拋磚引玉。

獨樂樂不如一起玩樂。

若有其他相關問題或者需求也可以郵件聯系俺探討。

郵箱地址是:
[email protected]

c語言智能指針 附完整示例代碼