6.02 Member Initializer Lists, Invariant Verification & constexpr Geometry Math
Executive Summary: Building geometric classes with constructors and member initializer lists. We analyze why member initializer lists are strictly more efficient than assignment inside the constructor body, and how to make geometry classes 100% constexpr for compile-time layout calculations.
💻 1. Annotated Source Code
#include <iostream> #include "Rectangle.h" using namespace std; int main() { Rectangle r1; Rectangle r2(5.0, 2.0); cout << "r1 area is " << r1.area() << endl; cout << "r2 area is " << r2.area() << endl; cout << "r1 perimeter is " << r1.perimeter() << endl; cout << "r2 perimeter is " << r2.perimeter() << endl; r1.setLength(22); r1.setWidth(12); cout << "r1 length is now " << r1.getLength() << endl; cout << "r1 width is now " << r1.getWidth() << endl; cout << "r1 area is NOW " << r1.area() << endl; return 0; }
#ifndef RECTANGLE_H #define RECTANGLE_H class Rectangle { public: Rectangle(); Rectangle(double length, double width); double getLength() const; double getWidth() const; void setLength(double length); void setWidth(double width); double area() const; double perimeter() const; private: double length; double width; }; #endif
#include "Rectangle.h" Rectangle::Rectangle() { this->length = 1.0; this->width = 1.0; }// no-arg ctor Rectangle::Rectangle(double length, double width) { this->length = length; this->width = width; }// end parameterized ctor double Rectangle::getLength() const { return length; } double Rectangle::getWidth() const { return width; } void Rectangle::setLength(double length) { this->length = length; } void Rectangle::setWidth(double width) { this->width = width; } double Rectangle::area() const { return length * width; } double Rectangle::perimeter() const { return 2 * length + 2 * width; //return 2 * (length + width); }
📐 2. Architecture & UML Class Model
<<class>>
Rectangle
Geometric Entity
Attributes / Data Members
-length : double
-width : double
Operations / Methods
+Rectangle()
+Rectangle(length: double, width: double)
+getLength() : double const
+getWidth() : double const
+setLength(length: double) : void
+setWidth(width: double) : void
+area() : double const
+perimeter() : double const
📚 3. Core C++ Concepts Deep-Dive
1. Member Initializer Lists vs Body Assignment
In a constructor, member initializer lists (Rectangle::Rectangle(double l, double w) : length(l), width(w) {}) initialize members directly when memory is allocated, avoiding double-initialization overhead.
📐 Rectangle Class UML Architecture
<<value-object>>
Rectangle
- length : double
- width : double
+ Rectangle()
+ Rectangle(l: double, w: double)
+ getLength() : double const
+ getWidth() : double const
+ area() : double const
+ perimeter() : double const
2. const Member Functions
Methods that do not modify class state (getLength() const, area() const) must be marked const, allowing them to be called on const objects in Flash ROM.
⚡ 4. Embedded Systems & Hardware Reality
1. constexpr Compile-Time Objects
Declaring geometry constructors and methods constexpr allows UI display coordinates, bounding boxes, and clip rects to be calculated during compilation, producing zero runtime CPU cycle overhead.
💡 5. Production-Ready Embedded Refactoring
100% constexpr UI bounding box class:
💡 Production-Ready Refactor
#include <cstdint> class BoundingBox { private: int16_t x_{0}; int16_t y_{0}; uint16_t width_{0}; uint16_t height_{0}; public: constexpr BoundingBox(int16_t x, int16_t y, uint16_t w, uint16_t h) noexcept : x_(x), y_(y), width_(w), height_(h) {} constexpr uint32_t area() const noexcept { return static_cast<uint32_t>(width_) * height_; } constexpr bool contains(int16_t px, int16_t py) const noexcept { return (px >= x_ && px < (x_ + width_) && py >= y_ && py < (y_ + height_)); } }; // Computed at compile-time and placed in Flash .rodata constexpr BoundingBox STATUS_BAR_RECT{0, 0, 320, 24}; constexpr uint32_t STATUS_BAR_AREA = STATUS_BAR_RECT.area(); // 7680
📝 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. Why is using a member initializer list (Class() : member(val) {}) preferred over body assignment (Class() { member = val; })?
Detailed Explanation:
Member initializer lists construct fields in-place. Body assignments default-construct the member first and then invoke the assignment operator, wasting cycles for complex objects.
Q2. In what order are member variables initialized in a C++ class?
Detailed Explanation:
C++ strictly specifies that members are initialized in the order of their declaration inside the class body. Compilers with
-Wreorder warn if the initializer list is in a different order.
Q3. What does marking a member function 'const' guarantee?
Detailed Explanation:
const member functions guarantee that this is a pointer to const, permitting calls on immutable objects stored in Flash memory.
Q4. Can a constexpr class constructor execute at runtime if passed non-constant arguments?
Detailed Explanation:
constexpr functions and constructors are versatile: when given compile-time constants they evaluate during compilation; when given runtime variables they execute normally at runtime.