9. Classes
9.1 Member Functions
9.1.1 Declare static any member function that does not require this. Alternatively, declare const any member function that does not modify the externally visible state of the object
A non-virtual member function that does not access the this pointer can be declared static. Otherwise, a function that is virtual or does not modify the externally visible state of the object can be declared const. The C++ language permits that a const member function modifies the program state (e.g. modifies a global variable, or calls a function that does so). However, it is recommended that const member functions are logically const also, and do not cause any side effects. The mutable keyword can be used to declare member data that can be modified in a const function, however, this should only be used where the member data does not affect the externally visible state of the object.
#include <cstdint> class C { public: explicit C (int32_t i) : m_i (i) , m_c (0) { } int32_t foo () // @@- Non-Compliant: should be static -@@ { C tmp (0); return tmp.bar (); } int32_t bar () // @@- Non-Compliant: should be const -@@ { ++ m_c; return m_i; } private: int32_t m_i; mutable int32_t m_c; };
References
- HIC++ v3.3 – 3.1.8
9.1.2 Make default arguments the same or absent when overriding a virtual function
The C++ language standard allows that default arguments be different for different overrides of a virtual function. However, the compiler selects the argument value based on the static type of the object used in the function call. This can result in confusion where the default argument value used may be different to the expectation of the user.
#include <cstdint> class Base { public: virtual void goodvFn (int32_t a = 0); virtual void badvFn (int32_t a = 0); }; class Derived : public Base { public: void goodvFn (int32_t a = 0) override; // @@+ Compliant +@@ void badvFn (int32_t a = 10) override; // @@- Non-Compliant -@@ }; void foo (Derived& obj) { Base& baseObj = obj; baseObj.goodvFn (); // calls Derived::goodvFn with a = 0 obj.goodvFn (); // calls Derived::goodvFn with a = 0 baseObj.badvFn (); // calls Derived::badvFn with a = 0 obj.badvFn (); // calls Derived::badvFn with a = 10 } References
- Do not use default arguments – 8.3.3
- HIC++ v3.3 – 3.3.12
9.1.3 Do not return non-const handles to class data from const member functions
A pointer or reference to non-const data returned from a const member function may allow the caller to modify the state of the object. This contradicts the intent of a const member function.
#include <cstdint> class C { public: C () : m_i (new int32_t) {} ~C() { delete m_i; } int32_t * get () const { return m_i; // @@- Non-Compliant -@@ } private: int32_t * m_i; C (C const &) = delete; C & operator = (C const &) & = delete; };
Exception
Resource handler classes that do not maintain ownership of a resource are exempt from this rule, as in this context the rule conflicts with Rule 9.1.1: ”Declare static any member function that does not require this. Alternatively, declare const any member function that does not modify the externally visible state of the object”, which takes precedence.
For example:
# include <cstdint> class D { public : D ( int32_t * p) : m_i (p) {} int32_t * get () const { return m_i; // Compliant } private : int32_t * m_i; };
References
- HIC++ v3.3 – 3.4.2
9.1.4 Do not write member functions which return non-const handles to data less accessible than the member function
Member data that is returned by a non-const handle from a more accessible member function, implicitly has the access of the function and not the access it was declared with. This reduces encapsulation and increases coupling.
#include <cstdint> class C { public: C () : m_i (0) {} int32_t & get () // @@- Non-Compliant -@@ { return m_i; } int const & get () const // @@+ Compliant +@@ { return m_i; } private: int32_t m_i; };
Exception
Non-const operator [] is exempt from this rule, as in this context the rule conflicts with Rule 13.2.4: ”When overloading the subscript operator (operator[]) implement both const and non-const versions”, which takes precedence.
For Example:
# include <cstdint> class Array { public : Array () ; int32_t & operator [] ( int32_t a) // Compliant: non-const version { return m_x[ a ]; } int32_t operator [] ( int32_t a) const // Compliant: const version { return m_x[ a ]; } private : static const int32_t Max_Size = 10; int32_t m_x [ Max_Size ]; };
References
- HIC++ v3.3 – 3.4.3
9.1.5 Do not introduce virtual functions in a final class
Declaring a class as final explicitly documents that this is a leaf class as it cannot be used as a base class. Introducing a virtual function in such a class is therefore redundant as the function can never be overridden in a derived class.
class Base { public: virtual void f1 (); // @@+ Compliant +@@ }; class Derived final : public Base { public: virtual void f2 (); // @@- Non-Compliant -@@ };
9.2 Bit-fields
9.2.1 Declare bit-fields with an explicitly unsigned integral or enumeration type
To avoid reliance on implementation defined behavior, only declare bit-fields of an explicitly unsigned type (uintN_t) or an enumeration type with an enumeration base of explicitly unsigned type.
#include <cstdint> enum E : uint8_t { ONE, TWO, THREE }; struct S { int32_t a : 2; // @@- Non-Compliant -@@ uint8_t b : 2; // @@+ Compliant +@@ bool c : 1; // @@- Non-Compliant -@@ char d : 2; // @@- Non-Compliant -@@ wchar_t e : 2; // @@- Non-Compliant -@@ E f : 2; // @@+ Compliant +@@ };
References
- JSF AV C++ Rev C – 154
- MISRA C++:2008 – 9-6-2
- MISRA C++:2008 – 9-6-3