Use of const

As we've seen the keyword const (meaning constant) can be used with primitive data types (i.e. const float PI = 3.14159;). But const can also be used with objects, member functions and function parameters. The keyword const should be used whenever possible to prevent incorrect data modification.

const objects

We can create const objects using declarations such as const DayOfYear jan1st( 1, 1); The DayOfYear object jan1st is not modifiable by any member or non-member function. Any attempt to modify this object (i.e. calling a mutator) such as jan1st.Set(1, 2); results in a compiler error like this myfile.cpp: In function `int main()': myfile.cpp:20: passing `const DayOfYear' as `this' argument of `void DayOfYear::Set(int, int)' discards qualifiers The phrase "discards qualifiers" is your hint that there is some violation of the const rules. We'll discuss "this" later

Temporary objects created by the compiler are const objects.

const member functions

The purpose of some member functions is to modify the object (e.g. mutators) for which they are called. Other member functions (e.g. accessors) do not modify the object.

How can the compiler tell the difference? Does it make a difference?

As we saw above under the const object section above it clearly makes a difference. The code jan1st.Set(1, 2); causes a compiler error because Set( ); is a mutator. How does the compiler know that?

The code jan1st.Output( ); should be allowed since the Output( ) function doesn't (or at least shouldn't) change the object on which it was called.

How does the compiler know? You have to tell it.

Adding the keyword const to the end of a member function's prototype tells the compiler:

I promise not to modify the object which calls me (directly or indirectly), and therefore I may be called by const objects.
"Directly" means changes a private data member in the body of the code.
"Indirectly" means call a non-const member function or pass the const object as a non-const paramter.

We're now ready for the final version of the DayOfYear class.

class DayOfYear { public: // constructors DayOfYear ( int initialMonth = 1, int initialDay = 1 ); // accessors int GetMonthNumber ( ) const; int GetDay( ) const; // mutators void Set( int newMonth, int newDay ); void Set( int newMonth ); // services void Input( ); void Output( ) const; private: int m_month; int m_day; };

Rules of Engagement -- Objects, Functions and Parameters

  1. const member functions may be invoked for const and non-const objects.
  2. non-const member functions can only be invoked for non-const objects.
    If a non-const member function is invoked on a const object, it is a compiler error.

  3. non-const objects may be used for const and non-const parameters
  4. const objects may only be used for const parameters

  5. const methods cannot invoke non-const methods

A short example

#include <iostream> #include "DayOfYear.h" using namespace std; // a function that inputs a DayOfYear // from the user void InputDayOfYear( DayOfYear& doy) { cout << "Please input the day of year: \n"; doy.Input( ) } // a function that prints a DayOfYear // to the standard output void OutputDayOfYear( const DayOfYear& doy) { cout << "The day of year is \n"; doy.Output( ); } int main ( ) { // a const object // must be initialized; cannot be changed const DayOfYear jan1st( 1, 1 ); // an object that is not constant // aka a "non-const" object DayOfYear today; // since non-const objects are changeable // they can be passed as const or non-const params // with no problems InputDayOfYear ( today ); OutputDayOfYear( today ); // similarly, all class methods may be called for // a non-const object today.GetMonthNumber ( ); // const method today.GetDay( ) // const method today.Set( 10, 5); // non-const method today.Set( 12 ); // non-const method today.Input( ); // non-const method today.Output() // const method // since const objects are not changeable // they may not be passed as non-const parameters InputDayOfYear ( jan1st ); // **** compiler error *** // but may be passed as const parameter OutPutDayOfYear ( jan1st ); // no problem // similarly, only const methods can be called // for a const object, since non-const methods // will try to change the object jan1st.GetMonthNumber ( ); // const method -- no problem jan1st.GetDay( ) // const method -- no problem jan1st.Set( 10, 5); // non-const method *** compiler error jan1st.Set( 12 ); // non-const method *** compiler error jan1st.Input( ); // non-const method *** compiler error jan1st.Output() // const method -- no problem return 0; }

A common, subtle coding error

This common mistake leads to a compiler error. Do you see why? int DayOfYear::GetDay ( ) const { if (m_day < 1 ) Set( m_month, 1 ); return m_day; } as does this one void Bob ( const DayOfYear& doy) { OutputDayOfYear ( doy ); cout << "Please enter your birth month and day \n"; int birthMonth, birthDay; cin >> birthMonth >> birthDay; doy.Set( birthMonth, birthDay ); }

A summary of the "Rules of Engagement"

Recall that objects are almost always passed to functions "by reference" to avoid unnecssary copying.

const objects non-const objects
Call const methods Yes Yes
Call non-const methods NO Yes
Pass as const parameter Yes Yes
Pass as non-const parameter NO Yes

const Implementation Guidelines

The appropriate use of const when designing a class interface is important for the appropriate implementation of your class methods. Follow these guidelines when desinging and implement your class.
  1. Experience has shown that properly designating methods and parameters as const from the very outset of your class design saves many hours of frustration later. Those who ignore the proper use of const at the start and then try to add constness as an afterthought spend many hours fixing compiler errors that could have been avoided.
    Use const correctly, and use it from the start
  2. Use const for member functions that do not change the data members of your class -- accessors.
  3. Don't use const for mutators.
  4. Class services may or may not change the object. Make them const accordingly.
  5. Use const for object parameters whenever possible.
  6. It's not wrong, but not customary, to use const when passing built-in data types by value or returning built-in types by value.

Last Modified: Monday, 28-Aug-2006 10:15:53 EDT