将 RAII 与字符指针结合使用

发布于 2024-08-19 21:13:59 字数 727 浏览 10 评论 0原文

我看到很多 RAII 示例类都围绕着文件句柄。

我尝试将这些示例改编为字符指针,但没有成功。

我正在使用的库具有获取字符指针地址的函数(声明如下 get_me_a_string(char **x))。 这些函数为该字符指针分配内存,并将其留给库的最终用户在自己的代码中清理它。

所以,我的代码看起来像这样......

char* a = NULL;
char* b = NULL;
char* c = NULL;

get_me_a_string(&a);
if(a == NULL){
    return;
}


get_me_a_beer(&b);
if(b == NULL){
    if(a != NULL){
        free(a);
    }
    return;
}


get_me_something(&c);
if(c == NULL){
    if(a != NULL){
        free(a);
    }
    if(b != NULL){
        free(b);
    }
    return;
}

if(a != NULL){
    free(a);
}
if(b != NULL){
    free(b);
}
if(a != NULL){
    free(b);
}

听起来 RAII 是我上面这个混乱的答案。 有人可以提供一个简单的 C++ 类来包装 char* 而不是 FILE* 吗?

谢谢

I see a lot of RAII example classes wrapping around file handles.

I have tried to adapt these examples without luck to a character pointer.

A library that I am using has functions that take the address of a character pointer (declared like get_me_a_string(char **x)).
These functions allocate memory for that character pointer and leave it up to the end user of the library to clean it up in their own code.

So, I have code that looks like this...

char* a = NULL;
char* b = NULL;
char* c = NULL;

get_me_a_string(&a);
if(a == NULL){
    return;
}


get_me_a_beer(&b);
if(b == NULL){
    if(a != NULL){
        free(a);
    }
    return;
}


get_me_something(&c);
if(c == NULL){
    if(a != NULL){
        free(a);
    }
    if(b != NULL){
        free(b);
    }
    return;
}

if(a != NULL){
    free(a);
}
if(b != NULL){
    free(b);
}
if(a != NULL){
    free(b);
}

It sounds like RAII is the answer for this mess that I have above.
Could someone provide a simple C++ class that wraps a char* rather than a FILE*?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

你又不是我 2024-08-26 21:13:59

标准库中已经有一些可用的东西:它被称为 std::string

编辑:根据新信息:

它将分配内存并填充它
向上。我可以将内容复制到
新的 std::string 对象但我仍然
必须释放内存
由函数分配。

对于实现者来说,这是一个糟糕的设计——分配的模块应该负责释放。

好的,现在我已经将其从系统中删除了:您可以使用 boost::shared_ptr 进行释放。

template<typename T>
struct free_functor
{
    void operator() (T* ptr)
    {
        free(ptr);
        ptr=NULL;            
    }
};
shared_ptr<X> px(&x, free_functor());

There's something available already in the standard library: it's called std::string.

Edit: In light of new information:

It will allocate memory and fill it
up. I could copy the contents into a
new std::string object but I'd still
have to free the memory that was
allocated by the function.

This is poor design on the implementor's part -- the module that allocates should be responsible for deallocation.

Okay, now that I've got that out of my system: you could use a boost::shared_ptr for freeing.

template<typename T>
struct free_functor
{
    void operator() (T* ptr)
    {
        free(ptr);
        ptr=NULL;            
    }
};
shared_ptr<X> px(&x, free_functor());
数理化全能战士 2024-08-26 21:13:59

一个非常基本的实现(您应该使其不可复制等)。

struct CharWrapper {
    char* str;
    CharWrapper(): str() {}  // Initialize NULL
    ~CharWrapper() { free(str); }
    // Conversions to be usable with C functions
    operator char**() { return &str; }
    operator char*() { return str; }
};

从技术上讲,这不是 RAII,因为正确的初始化发生在构造函数之后,但它会负责清理工作。

A very basic implementation (that you should make noncopyable etc).

struct CharWrapper {
    char* str;
    CharWrapper(): str() {}  // Initialize NULL
    ~CharWrapper() { free(str); }
    // Conversions to be usable with C functions
    operator char**() { return &str; }
    operator char*() { return str; }
};

This is technically not RAII, as proper initialization happens later than at the constructor, but it will take care of cleanup.

撕心裂肺的伤痛 2024-08-26 21:13:59

您可以尝试这样的方法:

template <typename T>
class AutoDeleteArray
{
public:
    explicit AutoDeleteArray(const T* ptr)
        : ptr_(ptr)
    {}
    ~AutoDeleteArray()
    {
        delete [] ptr_;
        // if needed use free instead
        // free(ptr_);
    }

private:
    T *ptr_;
};

// and then you can use it like:
{
    char* a = NULL;

    get_me_a_string(&a);
    if(a == NULL)
      return;

    AutoDeleteArray<char> auto_delete_a(a);
}

这不是最可靠的解决方案,但足以达到目的。

PS:我想知道 std::tr1::shared_ptr 与自定义删除器也可以工作吗?

You could try something like this:

template <typename T>
class AutoDeleteArray
{
public:
    explicit AutoDeleteArray(const T* ptr)
        : ptr_(ptr)
    {}
    ~AutoDeleteArray()
    {
        delete [] ptr_;
        // if needed use free instead
        // free(ptr_);
    }

private:
    T *ptr_;
};

// and then you can use it like:
{
    char* a = NULL;

    get_me_a_string(&a);
    if(a == NULL)
      return;

    AutoDeleteArray<char> auto_delete_a(a);
}

It is not the most reliable solution, but could be enough for the purpose.

PS: I'm wondering would std::tr1::shared_ptr with custom deleter work as well?

A君 2024-08-26 21:13:59

我认为 auto_ptr 是您想要的

,或者如果 auto_ptr 语义不适合您,则增强共享指针

i think auto_ptr is what you want

or boost shared_ptr if the auto_ptr semantics dont work for you

七颜 2024-08-26 21:13:59

使用普通 std::stringboost::scoped_array 用于本地数组,或 boost::shared_array 用于共享字符串(后者允许您提供自定义删除器来调用 free()。)

Either use plain std::string, or boost::scoped_array for local arrays, or boost::shared_array for shared strings (the latter allows you to provide custom deleter to call free().)

猫卆 2024-08-26 21:13:59

谢谢大家的回答。

不幸的是,我不能在这个项目中使用 boost 或其他库......所以所有这些建议对我来说都是无用的。

我已经研究过诸如 C 中的异常处理之类的东西,如下所示......
http://www.halfbakery.com/idea/C_20exception_20handling_20macros

然后我研究了为什么 C++没有像Java那样的finally并且遇到了这个RAII的东西。

我仍然不确定我是否会采用析构函数方式并仅使用 C++ 编写代码,还是坚持使用 C 异常宏(使用可怕的 goto:)

Tronic 建议如下所示。
对于 RAII,或者一般的析构函数,它们应该是防段错误的吗?我猜不是。

我唯一不喜欢的是我现在必须在 printf 语句中使用强制转换 (char*)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct CharWrapper {
    char* str;
    CharWrapper(): str() {}  // Initialize NULL
    ~CharWrapper() {
        printf("%d auto-freed\n", str);
        free(str);
    }
    // Conversions to be usable with C functions
    operator char*()  { return  str; }
    operator char**() { return &str; }
};

// a crappy library function that relies
// on the caller to free the memory
int get_a_str(char **x){
    *x = (char*)malloc(80 * sizeof(char));
    strcpy(*x, "Hello there!");
    printf("%d allocated\n", *x);
    return 0;
}


int main(int argc, char *argv[]){
    CharWrapper cw;
    get_a_str(cw);
    if(argc > 1 && strcmp(argv[1], "segfault") == 0){
        // lets segfault
        int *bad_ptr = NULL;
        bad_ptr[8675309] = 8675309;
    }
    printf("the string is : '%s'\n", (char*)cw);
    return 0;
}

Thanks everyone for your answers.

Unfortunately, I cannot use boost, or other libraries on this project... so all of those suggestions are useless to me.

I have looked at things like exception handling in C like here...
http://www.halfbakery.com/idea/C_20exception_20handling_20macros

And then I looked at why C++ doesn't have a finally like Java does and came across this RAII stuff.

I'm still not sure if I will go the destructor way and make the code C++ only, or stick with C exception macro's (which use the dreaded goto:)

Tronic suggested something like the following.
With RAII, or destructors in general, are they supposed to be segfault proof? I'm guessing not.

The only thing that I don't like is the fact that I now have to use a cast (char*) in my printf statements.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct CharWrapper {
    char* str;
    CharWrapper(): str() {}  // Initialize NULL
    ~CharWrapper() {
        printf("%d auto-freed\n", str);
        free(str);
    }
    // Conversions to be usable with C functions
    operator char*()  { return  str; }
    operator char**() { return &str; }
};

// a crappy library function that relies
// on the caller to free the memory
int get_a_str(char **x){
    *x = (char*)malloc(80 * sizeof(char));
    strcpy(*x, "Hello there!");
    printf("%d allocated\n", *x);
    return 0;
}


int main(int argc, char *argv[]){
    CharWrapper cw;
    get_a_str(cw);
    if(argc > 1 && strcmp(argv[1], "segfault") == 0){
        // lets segfault
        int *bad_ptr = NULL;
        bad_ptr[8675309] = 8675309;
    }
    printf("the string is : '%s'\n", (char*)cw);
    return 0;
}
ˉ厌 2024-08-26 21:13:59

另一种解决方案是这样的,这就是我用 C 编写此代码的方式:

char* a = NULL;
char* b = NULL;
char* c = NULL;

get_me_a_string(&a);
if (!a) {
    goto cleanup;
}

get_me_a_beer(&b);
if (!b) {
    goto cleanup;
}

get_me_something(&c);
if (!c) {
    goto cleanup;
}

/* ... */

cleanup:
/* free-ing a NULL pointer will not cause any issues
 * ( see C89-4.10.3.2 or C99-7.20.3.2)
 * but you can include those checks here as well
 * if you are so inclined */
free(a);
free(b);
free(c);

An alternative solution would be something like this, which is how I would write this code in C:

char* a = NULL;
char* b = NULL;
char* c = NULL;

get_me_a_string(&a);
if (!a) {
    goto cleanup;
}

get_me_a_beer(&b);
if (!b) {
    goto cleanup;
}

get_me_something(&c);
if (!c) {
    goto cleanup;
}

/* ... */

cleanup:
/* free-ing a NULL pointer will not cause any issues
 * ( see C89-4.10.3.2 or C99-7.20.3.2)
 * but you can include those checks here as well
 * if you are so inclined */
free(a);
free(b);
free(c);
深居我梦 2024-08-26 21:13:59

既然你说你不能使用 boost,那么编写一个不共享或传输资源的非常简单的智能指针并不难。

这是一些基本的东西。您可以指定删除器函子作为模板参数。我不是特别喜欢转换运算符,因此请使用 get() 方法。

随意添加其他方法,如release()和reset()。

#include <cstdio>
#include <cstring>
#include <cstdlib>

struct Free_er
{
    void operator()(char* p) const { free(p); }
};

template <class T, class Deleter>
class UniquePointer
{
    T* ptr;
    UniquePointer(const UniquePointer&);
    UniquePointer& operator=(const UniquePointer&);
public:
    explicit UniquePointer(T* p = 0): ptr(p) {}
    ~UniquePointer() { Deleter()(ptr); }
    T* get() const { return ptr; }
    T** address() { return &ptr; } //it is risky to give out this, but oh well...
};

void stupid_fun(char** s)
{
    *s = static_cast<char*>(std::malloc(100));
}

int main()
{
    UniquePointer<char, Free_er> my_string;
    stupid_fun(my_string.address());
    std::strcpy(my_string.get(), "Hello world");
    std::puts(my_string.get());
}

Since you are saying you can't use boost, it isn't very hard to write a very simple smart pointer which doesn't share or transfer resources.

Here's something basic. You can specify a deleter functor as a template parameter. I'm not particularly fond of conversion operators, so use the get() method instead.

Add other methods like release() and reset() at will.

#include <cstdio>
#include <cstring>
#include <cstdlib>

struct Free_er
{
    void operator()(char* p) const { free(p); }
};

template <class T, class Deleter>
class UniquePointer
{
    T* ptr;
    UniquePointer(const UniquePointer&);
    UniquePointer& operator=(const UniquePointer&);
public:
    explicit UniquePointer(T* p = 0): ptr(p) {}
    ~UniquePointer() { Deleter()(ptr); }
    T* get() const { return ptr; }
    T** address() { return &ptr; } //it is risky to give out this, but oh well...
};

void stupid_fun(char** s)
{
    *s = static_cast<char*>(std::malloc(100));
}

int main()
{
    UniquePointer<char, Free_er> my_string;
    stupid_fun(my_string.address());
    std::strcpy(my_string.get(), "Hello world");
    std::puts(my_string.get());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文