Sunday, June 13, 2010

Const vs Mutable, Pointers, Reinit Constructors and Statics

The "const" and "mutable" keyword
  • "const" means that the data related to the variable is to be left constant.
  • Only constant functions can be called on a const object.
  • "mutable" means that the data within a constant can be modified without changing the object ie the object is logically constant.
  • "mutable" is often abused. Its correct use is to allow for things like access counters and other data items that are not directly related to the objects real data. For example making the size of a toy class mutable is an abusive usage.
  • const tends to be referred to as a poison pill by in-experienced programmers who don't know about const_cast and how it works. Correct use of constants and const_cast can produce so very interesting coding patterns.
Constant Data, Pointers and references
const int  value0 = 1;       //constant data
const int* value1 = &value0; //a constant pointer
const int& value2 = value0;  //a constant reference
  • The Constant data indicates that the contents of the object or inbuilt type are not modifiable
  • The Constant pointer and references define that the data the pointer refers to is constant
Pointers which are constant
char* const string = "abc";  //a pointer constant
  • The Pointer Constant define that the pointer its self is not modifiable but the data is free to be messed with.
Const member functions
void function() const;
  • When "const" is applied to member functions it means that the action of the function will not change the object.
  • members of the object marked as "mutable" can me modified and maintain the "const" status of the object.
  • Constant member functions can not return non-constant references to members of the constant class.
  • Nothing prevents the constant member function from modifing items outside the objects local scope such as static members of the class.
  • By extension a const member function can const_cast its own this pointer and then modify the non-const version of its self! And it can also return this to the outside world. Doing this is considered bad form however in some systems bad usage of const and mutable has made it a requirement.
#include <iostream>
using namespace std;

class BaseA
{
public:
  static const int  staticConstIntA = 0; //init in global
  const   int       constA;           //init in constructor
  int               intA;
  static int        staticA;
  mutable int       mutableA;
  //const mutable int f; //BAD illogical
 
  void setA_Const(int _in) const
  { 
    //staticConstIntA = _in; //BAD const is hard set
    //constA = _in;          //BAD const is already hard set
    staticA  = _in;
    //intA     = _in;        //BAD a const member is modifing the object
    mutableA = _in;
  }

  void setA(int const _in)
  { 
    //staticConstIntA = _in; //BAD const is hard set
    //constA = _in;          //BAD const is already hard set
    staticA  = _in;
    intA     = _in;
    mutableA = _in;
  }

  //BAD a const member is directly returning a modifable piece of itself  
  //int& getA_Const(int _in) const
  //{ return intA; }

  int& getA_UnConst() const
  { 
    BaseA* self = const_cast<BaseA*>(this);
    self->intA = 10;
    return self->intA;
  }
  
  BaseA(int _in) : constA(_in) 
  {
    //staticConstIntA = _in; //BAD const is hard set
    //constA = _in;          //BAD const is already hard set
    staticA  = _in;
    intA     = _in;
    mutableA = _in;
  }
};

int BaseA::staticA; 

int main()
{
  // *********normal class*************** //
  BaseA normalA(1);

  //normalA.constA = 10;         //BAD member is const
  //BaseA::staticConstIntA = 10; //BAD member is const
  normalA.intA = 1;
  normalA.mutableA = 1;

  normalA.setA(10);       
  normalA.setA_Const(10); 
  
  // *********constant class*************** //
  const BaseA  constA(3);

  //constA.intA = 10;    //BAD entire class is const making normal members also const
  constA.mutableA = 10;  //OK a mutable member can be modifed.

  //constA.setA(10); //BAD member function is not const
  constA.setA_Const(10); //GOOD  member function is const, and modifing a const class 

  // *********constant refed class*************** //
  const BaseA& refA = normalA;

  //constA.intA = 10;    //BAD entire class is const making normal members also const
  refA.mutableA = 10;  //OK a mutable member can be modifed.

  //constA.setA(10); //BAD member function is not const
  refA.setA_Const(10); //GOOD  member function is const, and modifing a class static

  // *********pointers to const class*************** //
  BaseA* ptrA = new BaseA(2);
  const BaseA* constPtrA = new BaseA(4);

  //constPtrA->intA = 1;    //BAD a const pointer means the DATA not the POINTER 
  constPtrA->mutableA = 1;

  //constPtrA->setA(10);   //BAD member function is not const
  constPtrA->setA_Const(10); //GOOD  member function is const, and modifing a const class

  constPtrA = ptrA;         //GOOD a const pointer means the DATA not the POINTER
  //const BaseA* ptrB = new(constPtrA) BaseA(20); //BAD reinit constructors dont work on const pointers

  // *********pointers which is const to class*************** //
  BaseA* const ptrConstA = new BaseA(5);

  ptrConstA->intA = 1;     //GOOD a pointer const means the POINTER not the DATA
  ptrConstA->mutableA = 1;

  ptrConstA->setA(10);       //GOOD a pointer const means the POINTER not the DATA
  ptrConstA->setA_Const(10); //GOOD a pointer const means the POINTER not the DATA

  //ptrConstA = ptrA;       //BAD a pointer const means the POINTER not the DATA
  const BaseA* ptrB = new(ptrConstA) BaseA(20); //GOOD reinit constructors DO work on pointers which are constant

  // *********UNLOCKING the const posion pill*************** //
  const BaseA  constB(3);  
  cout << "before unlocking: " << constB.intA << endl;
  int& unlockedA = constB.getA_UnConst();
  cout << "after unlocking: " << constB.intA << endl;
}

No comments:

Post a Comment