In a const
-correct class, all member functions which don't change logical state have this
cv-qualified as const
, indicating that they don't modify the object (apart from any mutable
fields, which can freely be modified even in const
instances); if a const
cv-qualified function returns a reference, that reference should also be const
. This allows them to be called on both constant and non-cv-qualified instances, as a const T*
is capable of binding to either a T*
or a const T*
. This, in turn, allows functions to declare their passed-by-reference parameters as const
when they don't need to be modified, without losing any functionality.
Furthermore, in a const
correct class, all passed-by-reference function parameters will be const
correct, as discussed in Const Correct Function Parameters
, so that they can only be modified when the function explicitly needs to modify them.
First, let's look at this
cv-qualifiers:
// Assume class Field, with member function "void insert_value(int);".
class ConstIncorrect {
Field fld;
public:
ConstIncorrect(Field& f); // Modifies.
Field& getField(); // Might modify. Also exposes member as non-const reference,
// allowing indirect modification.
void setField(Field& f); // Modifies.
void doSomething(int i); // Might modify.
void doNothing(); // Might modify.
};
ConstIncorrect::ConstIncorrect(Field& f) : fld(f) {} // Modifies.
Field& ConstIncorrect::getField() { return fld; } // Doesn't modify.
void ConstIncorrect::setField(Field& f) { fld = f; } // Modifies.
void ConstIncorrect::doSomething(int i) { // Modifies.
fld.insert_value(i);
}
void ConstIncorrect::doNothing() {} // Doesn't modify.
class ConstCorrectCVQ {
Field fld;
public:
ConstCorrectCVQ(Field& f); // Modifies.
const Field& getField() const; // Doesn't modify. Exposes member as const reference,
// preventing indirect modification.
void setField(Field& f); // Modifies.
void doSomething(int i); // Modifies.
void doNothing() const; // Doesn't modify.
};
ConstCorrectCVQ::ConstCorrectCVQ(Field& f) : fld(f) {}
Field& ConstCorrectCVQ::getField() const { return fld; }
void ConstCorrectCVQ::setField(Field& f) { fld = f; }
void ConstCorrectCVQ::doSomething(int i) {
fld.insert_value(i);
}
void ConstCorrectCVQ::doNothing() const {}
// This won't work.
// No member functions can be called on const ConstIncorrect instances.
void const_correct_func(const ConstIncorrect& c) {
Field f = c.getField();
c.do_nothing();
}
// But this will.
// getField() and doNothing() can be called on const ConstCorrectCVQ instances.
void const_correct_func(const ConstCorrectCVQ& c) {
Field f = c.getField();
c.do_nothing();
}
We can then combine this with Const Correct Function Parameters
, causing the class to be fully const
-correct.
class ConstCorrect {
Field fld;
public:
ConstCorrect(const Field& f); // Modifies instance. Doesn't modify parameter.
const Field& getField() const; // Doesn't modify. Exposes member as const reference,
// preventing indirect modification.
void setField(const Field& f); // Modifies instance. Doesn't modify parameter.
void doSomething(int i); // Modifies. Doesn't modify parameter (passed by value).
void doNothing() const; // Doesn't modify.
};
ConstCorrect::ConstCorrect(const Field& f) : fld(f) {}
Field& ConstCorrect::getField() const { return fld; }
void ConstCorrect::setField(const Field& f) { fld = f; }
void ConstCorrect::doSomething(int i) {
fld.insert_value(i);
}
void ConstCorrect::doNothing() const {}
This can also be combined with overloading based on const
ness, in the case that we want one behaviour if the instance is const
, and a different behaviour if it isn't; a common use for this is constainers providing accessors that only allow modification if the container itself is non-const
.
class ConstCorrectContainer {
int arr[5];
public:
// Subscript operator provides read access if instance is const, or read/write access
// otherwise.
int& operator[](size_t index) { return arr[index]; }
const int& operator[](size_t index) const { return arr[index]; }
// ...
};
This is commonly used in the standard library, with most containers providing overloads to take const
ness into account.