No Privacy in C++

Posted in software by Christopher R. Wirz on Mon Sep 11 2017



C++ is fast and efficient - partly due to how it allocates continuous memory with as few gaps as possible. The declaration files are referenced by other parts of the code because it tells the compiler how memory is laid out. This means that the implementation is completely hidden, but private fields aren't truly private.

Note: This article assumes you have all declaration (.h / header) files. Otherwise you have to fuzz memory.

Suppose I have just written a simple class to prove that stack allocated objects are destroyed when they go out of scope.


// Program.cpp
#include "stdafx.h"
#include "MyClass.h"

using MClass = MyNamespace::MyClass<int>;

int main()
{
	for (int n = 0; n < 8; n++) {
		MClass m = MClass();
		m.PrintWithCount();
	}
	getchar();
    return 0;
}

MyClass must be declared in the "MyClass.h" file.


// MyClass.h
#pragma once
#ifndef MY_CLASS_H
#define MY_CLASS_H

#include <string>

namespace MyNamespace {
	
	template<typename T>
	class MyProp {
	public:
		MyProp() : MyProp<T>(0) {} // required default ctor
		MyProp(T v) { this->val = v; }
		T get_Val() const { return this->val; }

	private:
		T val;
	};

	template<typename T>
	class MyClass {
	public:
		MyClass();
		// Prints info on the object
		void Print() const;
		// Prints info on the object
		// and how many other objects are in memory
		void PrintWithCount() const;
		// Destroying the object reduces the count
		~MyClass();

	private:
		MyProp<T> prop;
		std::string time;
	};
}

#endif // !MY_CLASS_H

This is pretty straight forward. MyProp is defined in the .h file and has no corresponding .cpp file, but MyClass still needs a .cpp file for definition. This is where a lot of the heavy lifting happens - so pay close attention.


// MyClass.cpp
#include "stdafx.h"
#include <iostream>
#include <string>
#include <stdio.h>
#include <time.h>
#include "MyClass.h"

/*
 * Gets the current date and time as a string
 */
const std::string currentDateTime() {
	time_t now = time(0);
	char buf[80];
	tm tstruct;
	localtime_s(&tstruct, &now);

	// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
	strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
	return buf;
}

namespace MyNamespace {
	
	// fileprivate variables
	int myclass_id = 0;
	int myclass_count = 0;

	template<typename T>
	MyClass<T>::MyClass()
	{
		myclass_count++;
		myclass_id++;
		this->prop = MyProp<T>(myclass_id);
		this->time = currentDateTime();
	}

	template<typename T>
	struct MyProp_def {
		T val;
	};

	template<typename T>
	struct MyClass_def {
		MyProp<T> id;
		std::string time;
	};

	template<typename T>
	void print_my_class(const MyClass<T> *const t) {
		MyClass_def<T> c = ((MyClass_def<T>*)t)[0];
		MyProp_def<T> p = ((MyProp_def<T>*)(&(c.id)))[0];
		printf("MyClass(%d) (created on %s)", p.val, c.time.c_str());
	}

	template<typename T>
	void MyClass<T>::Print() const
	{
		print_my_class<T>(this);
		printf("\n");
	}

	template<typename T>
	void MyClass<T>::PrintWithCount() const
	{
		print_my_class(this); // <T> is not needed by some compilers
		printf(" is part of %d objects\n", myclass_count);
	}

	template<typename T>
	MyClass<T>::~MyClass()
	{
		myclass_count--;
	}

	template MyClass<int>;
}

The application compiles and runs, producing the following output:

MyClass(1) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(2) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(3) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(4) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(5) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(6) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(7) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(8) (created on 2017-09-11.01:55:39) is part of 1 objects

We can see that inside each loop, there is only one object in memory. Good: this means that object destruction is taking place before the next loop - when the object goes out of scope. That is one of the great features of stack allocated objects.

The file-private method print_my_class is displaying private fields of the object! This is done because of the consistency by which the MyClass object is sequentially laid out in memory. While the printf statement isn't very complicated, imagine how convenient this must be for code re-use for more complicated logic. I've actually done this for some pretty complicated/lengthy methods (it's a dirty way to stay const-compliant), but this is an article, not source code.

Speaking of code, let's get back to it... We were discussing this snipplet here:


template<typename T>
struct MyProp_def {
	T val;
};

template<typename T>
struct MyClass_def {
	MyProp<T> id;
	std::string time;
};

template<typename T>
void print_my_class(const MyClass<T> *const t) {
	MyClass_def<T> c = ((MyClass_def<T>*)t)[0];
	MyProp_def<T> p = ((MyProp_def<T>*)(&(c.id)))[0];
	printf("MyClass(%d) (created on %s)", p.val, c.time.c_str());
}

Let's break it down:


template<typename T>
void print_my_class(const MyClass<T> *const t) {

	// Get the value at the pointer
	MyClass<T> value_of_t = *t;
	
	// Cast the reference to the value as a pointer
	MyClass_def<T>* pointer_to_struct_of_t = (MyClass_def<T>*)(&value_of_t);
	
	// Get the value at the pointer
	MyClass_def<T> struct_of_t = pointer_to_struct_of_t[0];
	// can also be written 
	// MyClass_def<T> struct_of_t = *pointer_to_struct_of_t;
	// but it's preference... whichever is easier to read
	
	// Continue the code as usual
	MyClass_def<T> c = struct_of_t;
	
	// The same logic could be applied here, but there's an easier way
	MyProp_def<T> p = ((MyProp_def<T>*)(&(c.id)))[0];
	
	// Use the values provided by the defined structs.
	printf("MyClass(%d) (created on %s)", p.val, c.time.c_str());
}

As stated in the code comments, there is an easier way. Even better, the code snipplet can be restructured as follows:


template<typename T>
struct MyProp_def {
	T val;
};

template<typename T>
struct MyClass_def {
	MyProp_def<T> id;
	std::string time;
};

template<typename T>
void print_my_class(const MyClass<T> *const t) {
	MyClass_def<T> c = *((MyClass_def<T>*)t);
	printf("MyClass(%d) (created on %s)", c.id.val, c.time.c_str());
}

The application compiles and runs, producing the following output:

MyClass(1) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(2) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(3) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(4) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(5) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(6) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(7) (created on 2017-09-11.01:55:39) is part of 1 objects
MyClass(8) (created on 2017-09-11.01:55:39) is part of 1 objects

I'm seeing something here... Since memory is laid our sequentially, the MyProp class can just be a set of ordered values within the main struct. We can really just write


template<typename T>
struct MyClass_def {
	T id;
	std::string time;
};

template<typename T>
void print_my_class(const MyClass<T> *const t) {
	MyClass_def<T> c = *((MyClass_def<T>*)t);
	printf("MyClass(%d) (created on %s)", c.id, c.time.c_str());
}

Another way to write this could be as follows:


template<typename T>
struct MyClass_def {
	T id;
	std::string time;
};

template<typename T>
void print_my_class(const MyClass<T> *const t) {
	MyClass_def<T>* c = (MyClass_def<T>*)t;
	printf("MyClass(%d) (created on %s)", c->id, c->time.c_str());
}

It almost makes you wonder why the fields are marked as private at all.