GString::GString(constchar* CstyleString)
{
cout << "CTOR (" << CstyleString << ")" << endl;
if (CstyleString == nullptr)
CstyleString = "";
size = strlen(CstyleString);
// +1 for the NULL character at the end (/0)
mainString = newchar[size + 1];
for (int i = 0; i < size; i++)
mainString[i] = CstyleString[i];
// Now 'size' is the last element, that must be the NULL character
mainString[size] = '\0';
cout << "CONSTRUCED (" << mainString << ")" << endl << endl;
}
GString::GString(const GString& copy) : GString(copy)
{
cout << "COPY CTOR" << endl;
}
###############
int main()
{
GString my = "hello";
GString my2(my);
return 0;
}
During the development of the class I've had the need to make another separate function which would have constructed my GString.
For example, I have made a function which deletes all the occurrences of a given character from the beginning and the end of a string.
Example:
......hello........ ---> hello
This function has to overwrite the already existing string. So I should have done something like:
1 2
delete[] mainString;
mainString = result;
To avoid this repetitive process, I thought to make a separated function like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
void GString::Create(constchar* source)
{
if(mainString)
delete[] mainString;
if (source == nullptr)
source = "";
size = strlen(source);
// +1 for the NULL character at the end (/0)
mainString = newchar[size + 1];
for (int i = 0; i < size; i++)
mainString[i] = source[i];
// Now 'size' is the last element, that must be the NULL character
mainString[size] = '\0';
}
So that I could just have written:
Create(result);
So I decided to use this function also in my default GString constructor.
> I have made a function which deletes all the occurrences of a given character from
> the beginning and the end of a string. This function has to overwrite the already existing string.
> So I should have done something like:
1 2
> delete[] mainString;
> mainString = result;
The size of the trimmed string would not be greater than the size of the original; the buffer can be reused.
Something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
GString& GString::trim( char c )
{
char* left = mainString ;
while( *left == c ) ++left ;
char* right = mainString + size - 1 ;
while( right > left && *right == c ) --right ;
std::move( left, right+1, mainString ) ;
size = right - left + 1 ;
mainString[size] = 0 ;
return *this ;
}
However, that algorithm, as you pointed out, can save us from deleting the entire string.
However, this was a lucky situation.
But when it comes to other kind of algorithms, is that technique of making a separate Create() function so that it can check whether the string is a new or an existing one any good?
> is that technique of making a separate Create() function
> so that it can check whether the string is a new or an existing one any good?
Create is a bad name for this; something like assign would be more appropriate.
Assuming that an overloaded assignment operator is correctly implemented, this is what it would do.
In most situations, making the string larger would involve dynamically allocating a larger buffer and then replacing the existing buffer with the newly allocated buffer. For instance:
GString GString::SubString(constint beginIndex, constint endIndex) const
{
// TO DO: *** validate that beginIndex and endIndex are within range***
// The last "+1" is for the NULL character (\0)
char* tmp = newchar[(endIndex - beginIndex) + 1 + 1]; // *** this memory is never releasedfor (int i = beginIndex, j = 0; i <= endIndex; i++, j++)
tmp[j] = mainString[i];
tmp[(endIndex - beginIndex) + 1] = '\0';
return GString(tmp);
}
> I call the GString constructor passing the tmp as parameter.
> I can't free that memory neither before the return (otherwise it will return an invalid string) nor after.
Yes.
"In most situations, making the string larger would should involve dynamically allocating a larger buffer and then replacing the existing buffer with the newly allocated buffer." http://www.cplusplus.com/forum/general/191860/#msg925317
A few more constructors would help simplify the code. For instance:
> The only assignment operator I have takes a GString object, while tmp is a char*
> You mean... the compiler transforms that line of code from result = tmp; to result = GString(tmp);
> is there a solid rule for this to happen? Is there any documentation?