11. Member Access Control
11.1 Access Specifiers
11.1.1 Declare all data members private
If direct access to the object state is allowed through public or protected member data, encapsulation is reduced making the code harder to maintain. By implementing a class interface with member functions only, precise control is achieved over modifications to object state as well as allowing for pre and post conditions to be checked when accessing data.
#include <string> #include <cassert> class C { public: C (); std::string m_id; // @@- Non-Compliant -@@ }; class D { public: D (); std::string const & getId () const { assert (! m_id.empty () && "Id not yet specified"); return m_id; } private: std::string m_id; // @@+ Compliant +@@ };
Exception
Class types declared with the class key struct in an extern “C” block are intended to be compatible with C, and therefore such definitions are not covered by this rule.
For example:
extern "C" { struct S // Compliant { int i; int j; }; }
References
- HIC++ v3.3 – 3.4.1
11.2 Friends
11.2.1 Do not use friend declarations
Friend declarations reduce encapsulation, resulting in code that is harder to maintain.
class C { public: C & operator += (C const & other); friend C const operator + (C const &, C const & lhs); // @@- Non-Compliant -@@ }; class D { public: D & operator += (D const & other); }; D const operator + (D const & rhs, D const & lhs) // @@+ Compliant +@@ { D result (rhs); result += (lhs); return result; }
The following example demonstrates an additional complexity when adding friendship for functions that include universal reference parameters. In such cases an explicit declaration is required for all combinations of the lvalue reference and rvalue reference versions.
template <typename T, typename S> void foo (T && t, S && s) { t.foo (); } class A { private: void foo (); friend void foo(A &, A &); friend void foo(A &, A&&); friend void foo(A&&, A &); friend void foo(A&&, A&&); }; int main () { A a; foo(a, a); foo(a, A()); foo(A(), a); foo(A(), A()); }
Exception
In some cases friend declarations are part of the class interface, for example:
- serialization via stream input and output operators (operator << and operator >>)
- factory functions requiring access to private constructors
- an iterator class
For example:
# include <cstdint> class C { public : C (C const &) = default ; C (C &&) = default ; private : C ( int32_t ); friend C createClassC ( int32_t ); }; C createClassC ( int32_t i) { // pre condition check on ’i’ return C(i); }
References
HIC++ v3.3 – 3.4.4