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
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());
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.
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?
i think auto_ptr is what you want
or boost shared_ptr if the auto_ptr semantics dont work for you
对本地数组使用plain std::string
或boost :: scoped_array ,对于共享字符串使用boost :: shared_array (后者允许您提供自定义删除以调用free()
。)
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;
}
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);
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());
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.