DIMAJIX - software consulting (http://www.dimajix.de/)
 

Class Functions

The getters and setters from the last subsection are only one special case of general class functions or class methods. There are also some important rules for the general case, which are explained here.


Rule 43. A member function that does not change the state of an object (its member variables) is to be declared const.

This rule is part of the be const-correct-campaign that helps you as a developer to think about which methods should modify the state of an object and which methods may only access an object in a read-only manner. This in turn also helps the compiler with some optimisations in order to produce better and faster code. Plus the compiler also helps you to be const-correct when declaring const-methods, because he will throw an error message if you try to modify any member variable within a const-method.


Rule 44. If the behaviour of an object is dependent on data outside the object, this data is not to be modified by const member functions.

Although the compiler helps you not to change the state of an object in a const-method, it still is possible to modify any other data outside the object, even if you access this data through a pointer which is stored in the object. Of course this is to be avoided, because every other developer would expect that a const method does not change the state of an object in any way - not even in an indirect one by modifying external data.


Rule 45. A public method may never return a non-const pointer or reference to a class member.

It is absolutely perfect to return const references or const pointers to class members in getters (which are to be declared const) as this will produce faster code. But returning a non-const pointer to any member is like specifying the corresponding member as a public variable. This has to be avoided in most cases. A common exception is given below.


Exception to 45. A public method may return a non-const pointer or reference to a class member in the case of a container class.

If the class is a container class with a embedded objects, in some cases it is perfectly legal to return a non-const pointer or reference to these objects. This is especially true for the index operator ([]) in the case of fixed size arrays. Consider the following example:

template<class T, unsigned int size> Array {
private:
    T               m_Array[size];
    
public:
    inline T&       operator[](unsigned int );
    inline const T& operator[](unsigned int ) const;
};

template<class T,unsigned int size>
inline T& Array<T,size>::operator[](unsigned int i) {
    ASSERT(i < size);
    return m_Array[i];
}


template<class T,unsigned int size>
inline const T& Array<T,size>::operator[](unsigned int i) const {
    ASSERT(i < size);
    return m_Array[i];
}

In this case it is absolutely legal to return a non-const reference to the embedded array of objects. But you should also note, that there are two versions of the index operator declared: one const version and one non-const version. The first one also returns a const reference to the specified object, while the second declaration allows modification of the returned object. This dualism is often found with container classes that do have both types of access functions.


Rule 46. Forwarding functions are to be inline.

Forwarding functions or delegating functions are used in order to forward to function call to another (in most cases) embedded object that does the actual job. Think of an object that needs to be thread-safe and therefore has a lock and unlock method. Instead of implementing its own locking mechanism, it simply delegates any locking requests to an embedded mutex that will take care of it. In such a case the lock- and unlock-methods are so simple that it would make no sense at all not to inline them. Here is the example:

class MessageQueue {
private:
    Mutex           m_Mutex;
    
public:
    inline void     lock();
    inline void     unlock();
};

inline void MessageQueue::lock() {
    m_Mutex.lock();
}

inline void MessageQueue::unlock() {
    m_Mutex.unlock();
}

Kaya Memisoglu 2005-01-06