The role of the operator new
is to allocate a memory region (heap, not the execution stack) to the data and invoke in each cell the constructor of that data type. This means that each time the operator is executed new
occupies more memory and, as memory has limit, can not call new
the will without using delete
before missing memory for another new
.
Programs that need to make many memory allocations (such as symbolic math programs, web servers, and others) need to manage memory well, so they eliminate unnecessary data in case that memory region is needed for something later. Common programs that need to allocate a lot of data may also have problems like this, but it is less common.
Typically, in C++ the data allocated in a scope is out of focus on it, which is a good practice of programming based on variable life time control and ensures the misalignment of data no longer needed. In cases of data that need to persist in the above scopes, the data is already constructed in the variables of the original scope or copied to them.
To facilitate this control of life time, it is customary to use constructor of structures and objects of classes for the purpose of allocating the data whenever the objects are created and also the destructor to delete itlos, virtually automating the lifetime control of the data allocated in the heap without needing to remember to use new
and delete
at the right time.
With this, you could make a structure like this.
struct StringType {
private:
char *strChars ;
size_t strLen ;
void constroy( size_t len ){
strChars = new char[ (strLen=len)+1 ] ;
}
void constroyCopy( const StringType &string ){
constroy( string.StrLen ) ;
for( size_t index=0 ; index<=StrLen ; index++ ){
StrChars[index] = string.StrChars[index] ;
}
}
void destroy( void ){
delete strChars ;
}
public:
~StringType( void ){
destroy() ;
}
StringType( size_t maxLength ){
constroy(maxLength) ;
}
StringType( const StringType &string ){
constroyCopy(string) ;
}
resetString( size_t newMaxLength ){
destroy() ;
constroy(newMaxLength ) ;
}
resetString( const StringType &string ){
destroy() ;
constroyCopy(string) ;
}
size_t length( void ){
return strLen ;
}
const char* charPointer( void ){
return strChars ;
}
}
And then if you want the data to persist per copy you make a function that returns the structure, then it will be copied.
StringType char* enc( const char* Str ){
StringType _Str( strlen(Str) ) ;
for( size_t i=0 ; i<_Str.length() ; i++ ) _Str.charPointer[i] = Str[i]+4 ;
_Str.charPointer[len] = '\0';
return _Str ;
}
But if you prefer a better performance you can save the data directly into a destination structure by passing your address as argument.
void enc( const char* Str , String *Str_out ){
Str_out.resetString( strlen(Str) ) ;
for( size_t i=0 ; i<Str_out.length() ; i++ ) Str_out.charPointer[i] = Str[i]+4 ;
Str_out.charPointer[len] = '\0';
}
Note: in this section of your code
auto len = strlen(Str);
char* _Str = new char[len];
you allocated index cells 0, 1, ..., len-1
and in this
_Str[len] = '\0';
you saved in index cell len
, that doesn’t exist. To avoid possible errors (such as having something else allocated in that region of memory that will be lost), allocate one more cell. In the codes I’ve made, I’ve already automated it by allocating (strLen=len)+1
cells in the construction of the string-type structure.
Any doubt?
Edit: there is an alternative solution that preserves its way of programming, does not require an implemented structure and should perform better. All you have to do is add a parameter that is a target character array so that you write the solution to it instead of allocating and returning an array of characters. With this, you have full control of data creation and removal outside the function, and may involve scopes or other criteria that you consider most appropriate.
void enc( const char* Str , char* _Str_out ){
size_t len = strlen(Str) ;
for( size_t i=0 ; i<len ; i++ ) _Str_out[i] = Str[i]+4 ;
_Str_out[len] = '\0';
}
One more thing, you can do this without having to call the function that counts the number of characters in the string, thus improving the performance more.
void enc( const char* Str , char* _Str_out ){
size_t i ;
for( i=0 ; Str[i]!='\0' ; i++ ) _Str_out[i] = Str[i]+4 ;
_Str_out[i] = '\0' ;
}
Off-topic: half pedantic, but identifiers similar to
_Aaa
are reserved for the standard library and use this style in your code has undefined behavior.– Mário Feroldi