Project 6.01 Section 6 ⚡ Embedded Relevance: High Classes vs Structs Encapsulation Struct Padding Memory Alignment alignas

6.01 Classes vs Structs, Encapsulation & Member Variable Memory Alignment in RAM

Executive Summary: Exploring foundational C++ classes: access specifiers (public vs private), member functions, constructors, and encapsulation. We examine the exact memory layout of class instances in SRAM, how compiler alignment rules insert hidden padding bytes, and how to reorder member variables to minimize RAM consumption.

💻 1. Annotated Source Code

#include <iostream>
#include "Book.h"
using namespace std;

//void printBookDetails(const Book& book);

int main() {
	Book gameOfThrones("George Martin", "Game of Thrones",
		"Fantasy", 864);
	Book mathBook("James Stewart", "Calculus", "Math", 1392);
	Book cppBook("Bjarne Stroustrup", "The C++ Programming Language", "Programming", 1376);

	/*printBookDetails(gameOfThrones);
	printBookDetails(mathBook);
	printBookDetails(cppBook);*/

	gameOfThrones.printBookDetails();
	mathBook.printBookDetails();
	cppBook.printBookDetails();

	return 0;
}

//void printBookDetails(const Book& book) {
//	cout << book.getTitle() << " by " << book.getAuthor()
//		<< " has " << book.getNumPages() << " pages, "
//		<< "and its genre is " << book.getGenre() << endl;
//}
#ifndef BOOK_H
#define BOOK_H

#include <string>
using namespace std;

class Book {
	public:
		Book(string author, string title,
			string genre, int numPages);
		string getAuthor() const;
		string getTitle() const;
		string getGenre() const;
		int getNumPages() const;
		void printBookDetails() const;

	private:
		string author;
		string title;
		string genre;
		int numPages;

};

#endif
#include "Book.h"

#include <iostream>
using namespace std;

Book::Book(string author, string title,
	string genre, int numPages) {

	this->author = author;
	this->title = title;
	this->genre = genre;
	this->numPages = numPages;
}//end ctor

string Book::getAuthor() const {
	return author;
}

string Book::getTitle() const {
	return title;
}

string Book::getGenre() const {
	return genre;
}

int Book::getNumPages() const {
	return numPages;
}

void Book::printBookDetails() const {
	cout << title << " by " << author
		<< " has " << numPages << " pages, "
		<< "and its genre is " <<  genre<< endl;
}

📐 2. Architecture & UML Class Model

📐 Book Class Encapsulation & Natural Struct Alignment Model
+ Public - Private # Protected
<<class>> Book Encapsulated Entity
-author : std::string
-title : std::string
-numPages : int32_t
+Book(author: string, title: string, numPages: int)
+printBookDetails() : void const
+getAuthor() : std::string const
+getTitle() : std::string const
+getNumPages() : int32_t const

📚 3. Core C++ Concepts Deep-Dive

1. Classes vs Structs in C++

In C++, the only difference between class and struct is the default access level: members and base classes default to private in a class, and public in a struct.

📐 Book Class UML Architecture

<<entity>> Book
- author : string
- title : string
- numPages : int
+ Book(author, title, numPages)
+ printBookDetails() : void
+ getAuthor() : string const
+ getTitle() : string const
+ getNumPages() : int const

2. Encapsulation & Invariants

Private member variables enforce data hiding; public member functions validate inputs and preserve object invariants.

⚡ 4. Embedded Systems & Hardware Reality

1. Struct Padding & Hidden RAM Waste

On 32-bit microcontrollers, variables are aligned to their natural boundaries (4 bytes for uint32_t, 2 bytes for uint16_t). Declaring members in suboptimal order forces the compiler to insert padding bytes:

⚡ Embedded Hardware Code
struct BadOrder {
    uint8_t  flag1;    // 1 byte + 3 PADDING bytes!
    uint32_t address;  // 4 bytes
    uint8_t  flag2;    // 1 byte + 3 PADDING bytes!
}; // Total size: 12 bytes (6 bytes wasted on padding!)

struct GoodOrder {
    uint32_t address;  // 4 bytes
    uint8_t  flag1;    // 1 byte
    uint8_t  flag2;    // 1 byte + 2 PADDING bytes
}; // Total size: 8 bytes (33% RAM savings!)

💡 5. Production-Ready Embedded Refactoring

Optimized, compact embedded device metadata class:

💡 Production-Ready Refactor
#include <cstdint>
#include <string_view>
#include <array>

class EmbeddedBookRecord {
private:
    // Arranged from largest to smallest type to eliminate internal padding
    uint32_t page_count_{0};
    uint16_t publication_year_{0};
    uint8_t  edition_{1};
    uint8_t  is_checked_out_{0};
    std::array<char, 24> title_{};

public:
    constexpr EmbeddedBookRecord(uint32_t pages, uint16_t year, std::string_view title) noexcept
        : page_count_(pages), publication_year_(year) {
        size_t len = title.size() < 23 ? title.size() : 23;
        for (size_t i = 0; i < len; ++i) title_[i] = title[i];
        title_[len] = '\0';
    }

    constexpr uint32_t pages() const noexcept { return page_count_; }
    constexpr std::string_view title() const noexcept { return title_.data(); }
};

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. What is the ONLY difference between 'class' and 'struct' in C++?
A Members of a struct default to public, while members of a class default to private
B Structs cannot have member functions
C Classes are stored on the heap while structs are on the stack
D Structs cannot use templates
Detailed Explanation: In C++, class and struct are identical except for default member and inheritance access specifiers (private for class, public for struct).
Q2. Why does the order of member variable declarations in a class matter on 32-bit microcontrollers?
A Suboptimal member ordering forces the compiler to insert alignment padding bytes, bloating the object's RAM footprint
B Member order changes the clock frequency
C Variables declared first are read-only
D Member order changes function return types
Detailed Explanation: CPUs require aligned memory access. Interleaving 1-byte and 4-byte members creates wasted padding holes. Ordering by descending size minimizes padding.
Q3. On a 32-bit ARM processor, what is the sizeof a struct containing: 'uint8_t a; uint32_t b; uint8_t c;' without packing?
A 12 bytes (1 byte + 3 pad + 4 bytes + 1 byte + 3 pad)
B 6 bytes
C 8 bytes
D 4 bytes
Detailed Explanation: Due to 4-byte alignment, 3 padding bytes follow a and 3 padding bytes follow c, yielding $1+3+4+1+3 = 12$ bytes.
Q4. What compiler attribute or pragma disables alignment padding entirely for network/telemetry packets?
A #pragma pack(push, 1) or __attribute__((packed))
B #pragma inline
C __attribute__((aligned(32)))
D #pragma optimize
Detailed Explanation: __attribute__((packed)) or #pragma pack(1) instructs the compiler to omit padding, essential for matching exact binary network wire protocols.