notes/c++.md

2.3 KiB
Raw Blame History

title
C++

C++

Why operator[] returns a reference

class IntList
{
    private:
        int m_list[10]{};

    public:
        int& operator[] (int index);
};

int& IntList::operator[] (int index)
{
    return m_list[index];
}

Lets take a closer look at how list[2] = 3 evaluates. Because the subscript operator has a higher precedence than the assignment operator, list[2] evaluates first. list[2] calls operator[], which is defined to return a reference to list.m_list[2]. Because operator[] is returning a reference, it returns the actual list.m_list[2] array element. The partially evaluated expression becomes list.m_list[2] = 3, which is a straightforward integer assignment.

Any value on the left hand side of an assignment statement must be an l-value (which is a variable that has an actual memory address). Because the result of operator[] can be used on the left hand side of an assignment (for example list[2] = 3), the return value of operator[] must be an l-value. As it turns out, references are always l-values, because you can only take a reference of variables that have memory addresses. So by returning a reference, the compiler is satisfied returning an l-value.

Consider what would happen if operator[] returned an integer by value instead of by reference. list[2] would call operator[], which would return the value of list.m_list[2]. For example, if m_list[2] had the value of 6, operator[] would return the value 6. list[2] = 3 would partially evaluate to 6 = 3, which makes no sense. If you try to do this, the C++ compiler complains:

C:VCProjectsTest.cpp(386) : error C2106: '=' : left operand must be l-value

Taken from Overloading the Subscript Operator.

Necessity of a virtual destructor for an abstract class

A virtual destructor is essential for an abstract class because an object of a derived class is usually manipulated through the interface provided by its abstract base class. In particular it may be deleted through a pointer to a base class. Then the virtual function call mechanism ensures that the proper destructor is called. That destructor then implicitly invokes the destructors of its bases and members.