C++ Core Guidelines: Class Rules

A class is a user-defined type where the programmer can specify the representation, the operations, and the interface. The C++ core guidelines have a lot of rules for user-defined types.

The guidelines start with quite general rules but also include special rules for constructors and destructors, class hierarchies, overloading of operators, and unions.

Before I write about the special rules that are way more interesting, here are the eight general rules.

I will only write as much to the general class rules to make their intention clear.

General rules for classes

Observer

If data is related, you should put it into a struct or class; therefore, the second function is very easy to comprehend.

void draw(int x, int y, int x2, int y2);  // BAD: unnecessary implicit relationships
void draw(Point from, Point to);          // better

 

C.2: Use class if the class has an invariant; use struct if the data members can vary independently

An invariant is a logical condition that is typically established by a constructor.

struct Pair {  // the members can vary independently
    string name;
    int volume;
};

class Date {
public:
    // validate that {yy, mm, dd} is a valid date and initialize
    Date(int yy, Month mm, char dd);
    // ...
private:
    int y;
    Month m;
    char d;    // day
};

 

The class Date has the invariants y, m, and d. They are initialised and checked in the constructor. The data type Pair has no invariant; therefore it is a struct.

Due to the invariant, the class is easier to use. This is exactly the aim of the next rule.

C.3: Represent the distinction between an interface and an implementation using a class

The public methods are in this case the interface of a class and the private part is the implementation.

 

class Date {
    // ... some representation ...
public:
    Date();
    // validate that {yy, mm, dd} is a valid date and initialize
    Date(int yy, Month mm, char dd);

    int day() const;
    Month month() const;
    // ...
};

 

From a maintainability perspective, the implementations of the class Date can be changed without affecting the user.

C.4: Make a function a member only if it needs direct access to the representation of a class

If a function needs no access to the internals of the class, it should not be a member. Hence you get loose coupling and a change of the internals of the class will not affect the function.

C.5: Place helper functions in the same namespace as the class they support

Such a helper function should be in the namespace of the class.

namespace Chrono { // here we keep time-related services

    class Date { /* ... */ };

    // helper functions:
    bool operator==(Date, Date);
    Date next_weekday(Date);
    // ...
}
...
if (date1 == date2){ ... // (1)

 

Thanks to argument-dependent lookup (ADL) the comparison in (1) will additionally look for the identity operator in the Chrono namespace.

C.7: Don’t define a class or enum and declare a variable of its type in the same statement

I admit: defining a class and declaring a variable of its type in the same statement confuses me.

 

// bad
struct Data { /*...*/ } data{ /*...*/ }; 

// good
struct Data { /*...*/ };         
Data data{ /*...*/ };

C.8: Use class rather than struct if any member is non-public

This is a quite useful and often used convention. If a data type has private or protected members, make it a class.

C.9: Minimize exposure of members

This rule is also called data hiding and is one of the corner stones of object oriented class design. It means that you should think about two interfaces to your class. A public interface for the general use case and a protected interface for derived classes. The remaining members should be private.

I will continue with the more special rules. Here is an overview:

Let's continue with the two rules to concrete types.

Concrete types

First of all, I have to write about concrete types and regular types.

A concrete type is "the simplest kind of a class". It is often called a value type and is not part of a type hierarchy. Of course, an abstract type can not be concrete.

A regular type is a type that "behaves like an int" and has, therefore, to support copy and assignment, equality, and ordering. To be more formal. A regular type Regular supports the following operations.

  •  Copy and assignment
Regular a;
Regular a  = b;
~Regular(a);
a = b;
  • Equality

a == b;
a != b;
  • Ordering

a < b;

 

The built-in types are regular such as the container of the standard template library.

C.10: Prefer concrete types over class hierarchies

If you have no use-case for a class hierarchy, use a concrete type. A concrete type is way easier to implement, smaller and faster. You have not to think about inheritance, virtuality, references or pointers including memory allocation and deallocation. There will be no virtual dispatch and, therefore, no runtime overhead.

You just have a value.

C.11: Make concrete types regular

Regular types (ints) are easier to understand. They are per se intuitive. This means if you have a concrete type think about upgrading it to a regular type.

What's next

The next post will be about the lifetime of objects: create, copy, move, and destruct.

 

 

Thanks a lot to my Patreon Supporter: Eric Pederson.

 

Get your e-book at leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11 and C++14 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 500 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 265

All 426396

Currently are 136 guests and no members online