Operator-overloading surprisingly works but I can't figure out, why

Advertisements

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.)

>Solution :

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.

Leave a ReplyCancel reply