使用带有字符指针的 RAII
Posted
技术标签:
【中文标题】使用带有字符指针的 RAII【英文标题】:Using RAII with a character pointer 【发布时间】:2011-01-12 07:43:40 【问题描述】:我看到很多 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* 吗?
谢谢
【问题讨论】:
大多数分配内存的库都有释放它的功能。 (参见 XmStringCreate 和 XmStringFree)。你的图书馆有类似的释放功能吗? 是的,它确实有自己的免费功能,但由我来调用它。 【参考方案1】:既然你说你不能使用 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());
【讨论】:
【参考方案2】:另一种解决方案是这样的,这就是我用 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);
【讨论】:
在 C++ 中,这有一个问题,即由于异常,执行可能仍然永远无法达到清理。如果代码在任何地方都使用了异常,你还必须抛出一些 try 块来确保它。 是的,我一直在讨论这样做(尽管通过宏)... halfbakery.com/idea/C_20exception_20handling_20macros UncleBen:这实际上只是使用 C++ 编译器的纯 C 代码。 Windows 上的 Visual Studio 和 Linux 上的 G++。【参考方案3】:感谢大家的回答。
不幸的是,我不能在这个项目中使用 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;
【讨论】:
【参考方案4】:标准库中已经有一些可用的东西:它叫做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());
【讨论】:
我认为他被一个返回需要释放的 C 字符串的库所困。 我认为auto_ptr
不会起作用,因为它必须是free()
而不是delete
。不过,我相信boost::scoped_ptr
会让您指定自定义删除器。
实际上,我猜scoped_ptr
不允许自定义删除器。但是,shared_ptr
确实如此。
我从未建议过auto_ptr
——如果我的帖子给人这种感觉,我宁愿编辑它。是的,shared_ptr
是我所追求的。我的错。
@dirkgently:你说过“......可能比它的价值更麻烦。”事实上,如果它只做delete
,它就可以工作。出于同样的原因,boost::scoped_ptr
也不会。【参考方案5】:
你可以试试这样的:
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
是否也可以使用自定义删除器?
【讨论】:
【参考方案6】:一个非常基本的实现(您应该使其不可复制等)。
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,因为正确的初始化发生在构造函数之后,但它会负责清理。
【讨论】:
我已经做到了。我不知道如何实际使用它。我如何声明这种类型的对象(它实际上是一个对象,你使用了结构)。如何将所述声明的对象传递给这些库函数? CharWrapper str1; get_me_a_string(str1);放(str1);转换运算符可能有些问题,因此请考虑用访问器函数替换它们。 struct 和 class 之间的唯一区别是默认可见性。对于结构,它是公共的,对于类是私有的。 我刚刚测试了这个。它应该能够抵抗段错误吗?如果是这样,它就不起作用,因为内存没有释放。否则它似乎工作得很好。我唯一不喜欢的是,在调用 printf 时,我现在需要将其转换为 (char*)。调用其他函数似乎完全没有任何强制转换(工作中的 C++ 重载?) 如果函数采用 char* 或 char** 参数,则类型转换运算符允许它工作。由于 printf 是 vararg 函数(编译器不知道参数类型),因此无法进行自动转换。【参考方案7】:对于本地数组使用纯 std::string
或 boost::scoped_array,对于共享字符串使用 boost::shared_array(后者允许您提供自定义删除器以调用 free()
。)
【讨论】:
【参考方案8】:我认为 auto_ptr 是你想要的
如果 auto_ptr 语义不适合你,则提升 shared_ptr
【讨论】:
auto_ptr 删除内容,但他需要free()。 啊是的 - 你可以提供客户删除器但无论如何我都会投票给你的答案 auto_ptr 也不能很好地处理数组以上是关于使用带有字符指针的 RAII的主要内容,如果未能解决你的问题,请参考以下文章