Encapsulation in C++ and C

Definition

Encapsulation is a set of tools which can restrict access to data or methods that can manipulate it. Detailed definition of encapsulation can be found in my previous Medium post here. This article is focused on examples of encapsulation in C++ and C programming languages.

Encapsulation in C++

By default, data and methods in class are private — visible and modifiable only by object/class. Access level can be set to other values using appropriate key words provided by C++.

In C++, several access level specifiers are available, and they make data:

  • public —accessible by class, child classes, and from anywhere outside the class;
  • protected — accessible only by class and child classes;
  • private — accessible only by class.

To keep the examples short, only two levels (private and public) are demonstrated.

Basic encapsulation example

Inside the class Contact, variables and methods that are defined public can be accessed in the main (outside the class). Variables and methods which are defined to be private , can be read or modified only by class itself.

Trying to print or modify private int mobile_number in main will cause a compile time error because the access to private data in class is restricted.

Bypassing encapsulation with Friends (Legal way)

C++ language provides the programmer with a key word friend which can add exceptions to the general rules of data access restriction. If the function or class is defined as a friend of a class Contact — it can access protected or private data.

There are two main rules of friendship — it is not inheritable and not mutual. Also, making a friend does not modify access level in general — private data remains private having only this specific friend exception.

In this example, the function print_numbers() is a regular function and not a method of a class Contact . The only reason print_numbers() can access private data is its definition as a friend function. If the friend definition will be removed, the code will not compile.

Side note: friends should not be abused. Adding friend should be seen as adding an exception, not as general practice.

Bypassing encapsulation with Typecast and Pointers (Totally illegal way)

First of all, it is worth to mention that using pointers and typecasting this way is a bad practice. This method may or may not provide the wanted data. It is hard to read and hard to maintain. Nevertheless, it exists.

C++ inherits many powerful tools from C, one of which is typecasting. By default, all class variables and methods are private. At the same time, default access level for values stored instruct — public. It is totally possible to create a struct or fully public class with the same layout as class Contact and abuse typecasting to get access to private data.

Private data in class can be seen and modified if is treated as public data in struct

Encapsulation in C

Encapsulation is usually seen as one of the key OOP principles. However, it is not limited to OOP languages only. In C, encapsulation has been used for a long time despite the absence of private and public key words.

Side note: since C++ is highly compatible with C, techniques used for C can be used in both languages.

Private variables

In terms of encapsulation, all data in C can be seen as public by default. Struct variables can be made private by isolating their definition from main which can be achieved using separate headers and source files.

Here, in “private_var.c”, structure was defined. Since struct in C requires allocating and freeing memory, several helper functions were added.

In corresponding header file the structure Contact was declared, but its layout was not exposed.

Thus, “main.c” does not know about struct internals and attempt to read or modify private data will produce an error.

Accessing private variables with Pointers

Typecasting can be used as tool to bypass encapsulation in C as well as in C++, however, this method was shown already. Knowing that in struct data is laid out in the order it was declared, pointers and pointer arithmetic are suitable for achieving the goal.

Access to the struct variables is restricted. However, memory itself is not hidden or protected. Pointers are, well, pointing to some memory location, and it is possible to read and modify data stored at that address (assuming the address is a valid memory location). If a pointer will be assigned to the same address where the struct stores its data, data can be read. Using the same definitions for structure (same “.c” and “.h” file) and modified “main.c”, access restriction was bypassed.

mobile_number was read and home_number was read and modified using pointers and pointer arithmetic

Private functions

Functions, being extern by default, are visible across a so-called translation unit. In other words, if the files are compiled together into a single object file, any file will be able to execute any defined function from any other file. Making function static will restrict its visibility to the file it was defined in. Hence, several steps are needed to ensure function privacy:

  • function should be static either in source file or in corresponding header file;
  • function definition should be done in separate source file;

Here, in “private_funct.c”, static function print_numbers() was defined. Note, function delete_contact() makes a successive call to print_numbers() since they are located in the same file.

In corresponding header, print_numbers() was declared as static function.

Finally, “main.c” will successfully call print_numbers() indirectly through delete_contact() since both functions are defined in the same document. However, attempt to call print_numbers() directly from main will fail.

Accessing private functions

It is possible to run static function print_numbers() from main using goto or passing function pointer to main. Both options require modifications either in the “private_funct.c” source file, or in the function itself. Also, such methods are removing encapsulation and not bypassing it.

Conclusion

Encapsulation is not limited to OOP languages only. Modern OOP languages make usage of encapsulation convenient and natural. There are many ways to bypass encapsulation, and avoiding usage of questionable practices will help to keep it intact in both C and C++.