In my programming with C++ lessons I have to solve a practical problem. My program works, but I don't understand why.
The solution to the task is to execute the main function correctly
int main()
{
STRING M1 = "Max";
STRING M2("Moritz");
STRING M3;
M3 = M1 + " und " + M2;
std::cout << "Wilhelm Busch:\n" << M3 << "\n";
M1 = "______________";
M1[0] = '+';
M1[M1.GetLength() - 1] = M1[0];
std::cout << M1 << '\n';
return 0;
}
and produce the following console output:
Wilhelm Busch:
Max und Moritz
+------------+
We created a Class STRING
, which got several constructors:
class STRING
{
char *pBuf;
int Len;
public:
STRING() : pBuf(nullptr), Len(0){};
STRING(const char *pStr) : Len(strlen(pStr))
{
pBuf = new char[Len + 1];
strcpy(pBuf, pStr);
};
STRING(const STRING &other) : Len(other.Len)
{
pBuf = new char[Len + 1];
strcpy(pBuf, other.pBuf);
};
STRING(char C) : Len(6) //for testing purposes
{
for (int i = 0; i < 5; i++)
{
pBuf[i] = C;
}
pBuf[5] = '\0';
};
...
}
We wrote some operator overloads like these:
STRING &operator=(const STRING &other)
{
if (&other == this)
return *this;
delete[] pBuf;
this->Len = other.Len;
pBuf = new char[Len + 1];
strcpy(pBuf, other.pBuf);
return *this;
};
STRING operator+(const STRING &other)
{
char *temp = new char[other.Len + this->Len + 1];
strcpy(temp, this->pBuf);
strcat(temp, other.pBuf);
STRING newString(temp);
delete[] temp;
return newString;
};
Now in the main function I don't understand the line
M3 = M1 + " und " + M2;
It surprisingly does work correctly but my operator (STRING operator+(const STRING &other)
) does not support values from the datatype char*
which " und "
is, but only from the object STRING
.
There is no other operator operator+
in my code.
When I try to rewrite the same line like this:
M3 = M1 + 'D' + M2;
the program does compile but does not give any output and terminates after several seconds.
I would expect the result MaxDDDDDMoritz
if the compiler automatically creates a temporary instance of the class STRING
with the corresponding constructor.
(I added a constructor STRING(char C)
for the purpose of testing this line.)
The call to an operator overload works in exactly the same way as calls to other functions.
In particular your
STRING operator+(const STRING &other);
overload can be called with a STRING
argument. However, a const
lvalue reference (i.e. const&
) can bind to not only lvalue expressions, but also to rvalue expressions. That means it can also bind to a temporary object of type STRING
.
Generally in C++ when calling a function implicit conversions are always considered on the function argument to convert it to the target type. That's why e.g. void g(long);
can also be called as g(1)
, although 1
has type int
, not long
. The int
object in the function parameter is initialized by implicitly converting the long
value 1
to int
, which in this case results in the same value 1
for the int
object.
Implicit conversions don't happen only between fundamental types, but also from and to user-defined types like your STRING
.
In particular, for these so-called user-defined conversion sequences, among others, any constructor of the target class that takes a single argument and is not marked with the keyword explicit
can be considered for the conversion.
You have a constructor STRING(const char *);
that matches this. It can be used to convert a const char*
to a STRING
. This new STRING
object is then a temporary object living until the end of the (full-)expression in which it was created, i.e. the end of M3 = M1 + " und " + M2
, and the reference parameter of your operator+
will bind to this temporary STRING
object, which has the correct type for the reference.
Therefore the overload is viable and since there is no other viable overload, the compiler will use it like that.
The same happens with 'D'
since you have a STRING(char C);
constructor. You see that it still compiles. It then fails at runtime only because you forgot to initialize pBuf
in that constructor, i.e. the line pBuf = new char[Len + 1];
is missing. Therefore the constructor has undefined behavior for reading the pointer's value uninitialized.