Christopher said:
I need a smart char * class, that acts like a char * in all cases, but
lets you do some std::string-type stuff with it. (Please don't say to
use std::string - it's not an option...).
Why? I would really like to know.
This is my attempt at it,
but it seems to be lacking... I'm aware that strdup() is nonstandard
(and a bad idea for C++ code) - please just bear with me:
/* Assume relevant headers are included */
class char_ptr
{
char *value;
public:
char_ptr() : value(strdup("")) {}
~char_ptr() {free(value);}
char_ptr( const char *s ) : value(strdup(s?s:"")) {}
void operator= ( const char * s ) {
free( value );
value=strdup( s?s:"" );
};
I do not see undefined behaviour, but I do see that the assignment operator
is prone to self-destruct your string in
char_ptr x;
...
x = x; // <- bang. (This can occur in less abvious forms.)
Aside from ignoring the return value of strdup(), is there anything
that invokes undefined behavior in this definition? If there is not,
should the following code work or not?
char_ptr s( "Hello, world!\n" );
printf( "%s", s );
I'm aware of the very kludgy nature of this stuff; is there a (better)
way to accomplish this task? Can it be done at all?
Now, if this works then the reason would be that the compiler does not do
any type checking here. I ran a test:
#include <string.h>
#include <stdlib.h>
class char_ptr
{
char *value;
public:
char_ptr() : value(strdup("")) {}
~char_ptr() {free(value);}
char_ptr( const char *s ) : value(strdup(s?s:"")) {}
void operator= ( const char * s ) {
free( value );
value=strdup( s?s:"" );
}
};
int main ( void ) {
char_ptr s( "Hello, world!\n" );
printf( "%s", s );
}
This compiles on g++-3.4.0/Linux but segfaults. You need a cast:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
class char_ptr
{
char *value;
public:
char_ptr() : value(strdup("")) {}
~char_ptr() {free(value);}
char_ptr( const char *s ) : value(strdup(s?s:"")) {}
void operator= ( const char * s ) {
free( value );
value=strdup( s?s:"" );
}
operator const char * ( void ) const {
return value;
}
};
int main ( void ) {
char_ptr s( "Hello, world!\n" );
printf( "%s", (const char*)s );
}
This seems to work, but is flawed as pointed out above.
Now, if you need a cast anyway, why not use std::string?
Best
Kai-Uwe Bux
ps.: If you really insist, here is a version that I wrote long ago to
convince myself that I cannot do better than std::string.
#include <string>
class
minimal_string {
private:
char* data;
std::size_t size;
inline
void allocate ( size_t __size ) {
size = __size;
data = new char [ __size ];
}
inline
void deallocate ( void ) {
delete[] data;
}
inline
static
void memcopy ( const char* source, char* dest, size_t size ) {
std::char_traits<char>::copy( dest, source, size );
}
inline
void copy_string ( const char* str ) {
// WARNING: [no deallocation]
/*
| This is used in the constructor. Therefore,
| no deallocation can be done.
*/
if ( str != NULL ) {
std::size_t l ( std::char_traits<char>::length( str ) + 1 );
allocate( l );
memcopy( str, data, l );
} else {
// maybe, we should throw something ?
allocate( 1 );
*data = 0;
}
}
public:
minimal_string ( void ) {
allocate( 1 );
*data = 0;
}
minimal_string ( const char* str ) {
copy_string( str );
}
minimal_string ( const minimal_string & other ) {
std::size_t l ( other.length() );
allocate( l );
memcopy( other.data, data, l );
}
~minimal_string ( void ) {
delete [] data ;
}
const minimal_string & operator= ( const minimal_string & other ) {
if ( this != &other ) {
deallocate();
std::size_t l ( other.length() );
allocate( l );
memcopy( other.data, data, l );
}
return( *this);
}
void setString ( const char* str ) {
deallocate();
copy_string( str );
}
std::size_t length ( void ) const {
return( size );
//return( 1 + std::char_traits<char>::length( data ) );
}
operator const char* ( void ) const {
return( data );
}
}; // minimal_string
Beware, I wrote this before I knew anything about exception safe coding.
Therefore, it is very likely that this leaks memory when something is
thrown.