Month: July 2012

const-antly Changing

In addition to the virtual and inline method modifiers, C++ also allows the addition of the const modifier on class methods. The const modifier indicates the method does not alter the state of class member variables.

class MyNumber
{
public:
    int getValue() const
    {
        return mValue;
    }

    void setValue(int inValue)
    {
        mValue = inValue;
    }

private:
    int mValue;
};

The implementation of getValue(), as long as it is declared const, cannot alter the value of mValue; the method simply has read-only access to all member variables. Declaring a method as const is helpful because it allows us to handle read-only instances of the MyNumber class:

void printMyNumber(const MyNumber& inNumber)
{
    cout << inNumber.getValue();

    // We can't call inNumber.setValue() because our reference
    // is to a const MyNumber object and setValue() is not a
    // const method.
}

...

MyNumber x;
x.setValue(14);
// We pass in a reference to a constant object, x.
printMyNumber(x);
// We know the value of x is still 14.

But what if we want to use a locking mechanism to make our class thread-safe?

class MyLock
{
public:
    void lock();
    void unlock();
};

class MyNumber
{
public:
    int getValue() const
    {
        mLock.lock();
        const int val = mValue;
        mLock.unlock();
        return val;
    }

    void setValue(int inValue)
    {
        mLock.lock();
        mValue = inValue;
        mLock.unlock();
    }

private:
    MyLock mLock;
    int mValue;
};

Can we do this? Notice the lock() method on MyLock isn’t const, presumably because the internal state of the MyLock object isn’t preserved during its execution (obviously the state of the lock changes). The compiler will report an error when it encounters mLock.lock() within a const method. We could remove the const modifier from getValue(), but then we can no longer pass around const references or pointers to MyNumber when we need to retrieve its value.

We now have a question to consider: What do we mean when we say a const method does not alter the internal state of an object? From within MyNumber, we can say getValue() no longer preserves the state of MyNumber because the bits inside mLock are changing. But from another perspective (outside of MyNumber), does anyone really care about the changing state of mLock? It’s an internal object that is invisible as far as any code using MyNumber is concerned. It’s an implementation detail that remains hidden from the outside world. So how can we enjoy the benefits of keeping getValue() as a const-modified method.

Fortunately, C++ provides a solution to this problem. One possibility would be recasting the this pointer to effectively ignore the const modifier:

int MyNumber::getValue() const
{
    const_cast<MyNumber*>(this)->mLock.lock();
    int val = mValue;
    const_cast<MyNumber*>(this)->mLock.unlock();
    return val;
}

However, this is pretty ugly. A better solution is to simply declare the mLock member variable as mutable:

class MyNumber
{
    ...
private:
    mutable MyLock mLock;
    int mValue;
};

The mutable keyword lets the compiler know the state of the member variable can be altered even from within a const method. Obviously, the mutable modifier could be misused – why not declare all methods as const and all member variables as mutable? The intent is to use mutable only when changes to the internal state of an object cannot be observed from outside the object. Thread-safe locks could be one example of such a situation. The mutable keyword might also be used to update internal statistics for the class – so you could keep track of how many times the getValue() method had been called. You might also use the mutable keyword to cache the result of a computationally expensive operation:

class MyNumber
{
public:
    double getSquareRootValue() const
    {
        // Track some statistics.
        mSqrtCount++;

        // Cache the square root if we don't already have it.
        if (mSqrtValue < 0)
        {
            mSqrtValue = sqrt(mValue);
        }

        return mSqrtValue;
    }

    void setValue(double inValue)
    {
        mValue = inValue;
        mSqrtValue = -1;
    }

private:
    double mValue;
    mutable double mSqrtValue;
    mutable int mSqrtCount;
};

The use of mutable allows us to maintain what can be referred to as “conceptual const-ness” (or “logical const-ness”) instead of “bitwise const-ness”. That is to say, the bits within an object may change, but the conceptual (or observable) state of the object remains constant.

When virtual Functions Won’t Fall inline

C++ offers two useful modifiers for class methods: virtual and inline. A virtual method can inherit new behavior from derived classes, while the inline modifier requests that the compiler place the content of the method “inline” wherever the method is invoked rather than having a single instance of the code that is called from multiple places. You can think of it as simply having the compiler expand the content of your method wherever you invoke the method. But how does the compiler handle these modifiers when they are used together?

First, a sample class.

class Number
{
protected:
    int mNumber;

public:
    Number(int inNumber)
        : mNumber(inNumber)
    {
    }

    inline int getNumberInline() const
    {
        return mNumber;
    }

    virtual inline int getNumber() const
    {
        return mNumber;
    }
};

We can create an evaluation function to exercise our class:

void evalNumber(const Number* num)
{
    int val = num->getNumber();
    int inl = num->getNumberInline();
    printf("val = %d, inl = %d", val, inl);
}

And we can call our evaluation function, providing an instance of the Number class:

    Number* num = new Number(5);
    evalNumber(num);
    delete num;

The disassembly of evalNumber looks like this (using Microsoft Visual C++ 2008):

void evalNumber(const Number* num)
{
00CE2010  push        ebp  
00CE2011  mov         ebp,esp 
00CE2013  sub         esp,8 
    int val = num->getNumber();
00CE2016  mov         eax,dword ptr [num] 
00CE2019  mov         edx,dword ptr [eax] 
00CE201B  mov         ecx,dword ptr [num] 
00CE201E  mov         eax,dword ptr [edx] 
00CE2020  call        eax  
00CE2022  mov         dword ptr [val],eax 
    int inl = num->getNumberInline();
00CE2025  mov         ecx,dword ptr [num] 
00CE2028  mov         edx,dword ptr [ecx+4] 
00CE202B  mov         dword ptr [inl],edx 
    printf("val = %d, inl = %d", val, inl);
00CE202E  mov         eax,dword ptr [inl] 
00CE2031  push        eax  
00CE2032  mov         ecx,dword ptr [val] 
00CE2035  push        ecx  
00CE2036  push        offset __load_config_used+48h (0CE49D0h) 
00CE203B  call        dword ptr [__imp__printf (0CE724Ch)] 
00CE2041  add         esp,0Ch 
}
00CE2044  mov         esp,ebp 
00CE2046  pop         ebp  
00CE2047  ret              

You’ll notice that when invoking the virtual inline method the compiler inserts a call to the method’s implementation while the inline version of our function is expanded in-place. So why didn’t our virtual inline method get expanded in-place as well?

The reason lies in how virtual methods work. When a C++ compiler encounters a virtual method, it typically creates a virtual method table (or v-table) for the class, containing pointers to each virtual method for the class. When an instance of the class is created, it contains a pointer to the v-table. Invoking the virtual method requires a look-up in the v-table to retrieve the address for the correct implementation of the method. Instances of derived classes are simply able to point to a different v-table to override behavior in a base class. Understanding how v-tables work, it should be apparent why the compiler couldn’t expand the virtual inline method in place. It’s possible num pointed to an object derived from Number and the implementation of getNumber() had been overridden. In this case, the compiler had to go through the v-table to ensure it invoked the correct method implementation.

So does virtual inline buy us anything? As it turns out, the compiler can take advantage of the inline declaration when it can determine with certainty the type of the object being referenced.

    Number num2(5);
    int dblVal = num2.getNumber();
    printf("dblVal = %d", dblVal);

When we reference num2 as a local variable, the compiler can determine from the context that we are referencing an instance of the Number class and not another class derived from Number. This allows the compiler to generate the following code:

    Number num2(5);
009220B5  mov         dword ptr [num2],offset NumberDoubler::`vftable' (924798h) 
009220BC  mov         dword ptr [ebp-8],5 
    int dblVal = num2.getNumber();
009220C3  mov         edx,dword ptr [ebp-8] 
009220C6  mov         dword ptr [dblVal],edx 
    printf("dblVal = %d", dblVal);
009220C9  mov         eax,dword ptr [dblVal] 
009220CC  push        eax  
009220CD  push        offset __load_config_used+5Ch (9249E4h) 
009220D2  call        dword ptr [__imp__printf (92724Ch)] 
009220D8  add         esp,8

You can see the code for getNumber() has been expanded in-place. It’s important to realize the compiler can only make this optimization because it knows the object’s type with certainty and, therefore, doesn’t need to go through the v-table to call the method. Instead the inline method can be expanded in-place.