Skip to content

Chapter 11: C++ Basics for HPC

Source slides: V09_Cpp_Basics.pdf. Exercise: E09_Cpp_Basics.pdf. Code: examples_exercises/example01.cpp..example10.cpp, task_01.cpp, task_02.cpp and exercises_solutions/task_01_solution.cpp, task_02_solution.cpp (the Complex class).


1. Chapter Overview

C++ is the dominant language of CFD solvers (OpenFOAM, SU2, Code_Saturne, AMReX). It combines C-level performance with object-oriented abstractions for meshes, fields, boundary conditions. This chapter covers what an HPC user must know to read, modify and compile C++ code:

  • The build cycle: edit → compile (g++) → run.
  • Variables and primitive data types (int, char, float, double, bool).
  • Scope and namespaces (int a vs box::a vs global ::a).
  • Pointers (int *p), addresses (&x), dereferencing (*p).
  • Arrays and pointer arithmetic.
  • Conditionals (if/else, ?:), for/while loops.
  • Functions: declaration, definition, call by value vs call by reference vs call by pointer.
  • Classes and objects: class Complex { ... };, constructors, member functions, private/public.

Why it matters in HPC/CFD: every solver hot-loop is C/C++; understanding pointers and call-by-reference is essential to know why code is fast or slow and how to avoid copying multi-GB fields.

What the examiner asks (very high frequency):

  • "Compile and run example01.cpp."
  • "Predict the output of example10.cpp (call-by-value vs reference)."
  • "Difference between pointer and reference."
  • "Implement swap using pointers."
  • "Complete the Complex class."
  • "Predict size of int, char, float."

What you must master for top grade:

  • The compile command: g++ -O2 -std=c++17 file.cpp -o exe.
  • The pointer trio: & (address-of), * (declaration / dereference).
  • The 3 ways to pass to functions: by value (copy), by pointer (int*), by reference (int&).
  • Class anatomy: declaration / definition / constructor / member function / private vs public.
  • The Complex exercise — Memorise it.

2. Basics from Zero

A C++ program is a text file (.cpp) the compiler turns into a binary you can execute. Minimum example:

#include <iostream>
int main() {
    std::cout << "Hello World" << std::endl;
    return 0;
}

Compile and run:

g++ example01.cpp -o example01
./example01            # prints "Hello World"

C++ has strict typing: every variable has a fixed type known at compile time (int, char, float, double, bool). Sizes are platform-dependent but typical:

int    : 4 bytes (32-bit)
char   : 1 byte
float  : 4 bytes (32-bit IEEE-754)
double : 8 bytes (64-bit IEEE-754)
bool   : 1 byte

A pointer stores an address. int *p = &b; makes p point at b; *p reads back the value at that address.

A reference is an alias for an existing variable: int& r = b; — r is just another name for b. References are like "pointers that can never be null and never re-bind".

বাংলায়: Pointer হলো এমন একটা variable যার ভেতরে মান নয়, আরেকটা variable-এর মেমরি ঠিকানা থাকে — &b দিয়ে ঠিকানা নেওয়া হয়, *p দিয়ে সেই ঠিকানায় গিয়ে মান পড়া হয়। Reference হলো একই variable-এর আরেকটা নাম — null হতে পারে না, পরে অন্য কিছুর দিকে ঘোরানোও যায় না। পরীক্ষায় "pointer vs reference" পার্থক্যটা প্রায় প্রতি বছর আসে, তিনটা পয়েন্ট মুখস্থ রাখুন: null সম্ভব কি না, re-bind সম্ভব কি না, dereference করতে * লাগে কি না।

Functions can take parameters by value (a copy is made — original untouched), by pointer (caller passes &x, callee writes *p = ...), or by reference (cleanest syntax; modifies original).

বাংলায়: Function-এ argument পাঠানোর তিনটা পথ: by value মানে কপি যায় (মূলটা অক্ষত), by pointer মানে ঠিকানা যায় (callee *p দিয়ে মূলটা বদলাতে পারে), আর by reference মানে আসল variable-টাই অন্য নামে যায়। HPC-তে এটা শুধু syntax নয়, performance-এর প্রশ্ন — কয়েক GB-র field কপি করা মানে simulation-এর সর্বনাশ। পরীক্ষায় example10.cpp-এর output predict করতে দেয়: value বদলায় না, reference বদলায়।

Classes group data + functions. The lecture's Complex class stores a real and imaginary part and provides display, add, multiply, modulus.

Real-life analogy. Pointers = postal addresses (you can copy the address; everyone holding the address visits the same house). References = nicknames (Mr. President = Joe Biden — both refer to the same person; you can't make "Mr President" point to someone else later). Classes = forms with fields plus filing instructions stapled together.

Real-life HPC use. A CFD field of 1e9 cells is 8 GB; passing it by value to a function would copy 8 GB; by reference passes a 8-byte address. Wrong choice = simulation 1000× slower.

What if you misunderstand? You write int *p; *p = 5; without setting p first → segmentation fault. Or you use = for compare in if(a=5) → always true (assignment). Or you forget ; after class definition → cryptic compile errors.


3. Hard English Made Easy

Hard Term Simple English বাংলা Example
Compiler Translates source to binary উৎস → বাইনারিতে অনুবাদ g++
Linker Joins object files into binary অবজেক্ট ফাইল যোগ করা ld
Header file Declarations ঘোষণাপত্র ফাইল <iostream>
Namespace Naming scope নাম-পরিধি std::cout
Scope Where a name is visible পরিধি local / global
Pointer Variable holding address অ্যাড্রেস ধরে রাখা ভেরিয়েবল int *p
Reference Alias for an existing var বিদ্যমান ভেরিয়েবলের নামান্তর int& r
Dereference Access value at pointer পয়েন্টার থেকে মান পড়া *p
Address-of Get the memory address মেমরির ঠিকানা নেওয়া &x
Stack / Heap Auto / dynamic memory অটো / ডায়নামিক মেমরি int x; / new int
Class Blueprint for objects অবজেক্টের নকশা class Complex
Object Instance of class ক্লাসের উদাহরণ Complex c(3,4)
Member Variable/function inside class ক্লাসের সদস্য c.real()
Constructor Special init function ইনিশিয়াল ফাংশন Complex(float,float)
Destructor Cleanup function ক্লিনআপ ফাংশন ~Complex()
Public / Private Access level পাবলিক / প্রাইভেট private:
Function declaration Signature only শুধু সিগনেচার bool is_prime(int);
Function definition Body বডি bool is_prime(int n){…}
Pass by value Copy the argument কপি পাঠানো void f(int)
Pass by reference Share the argument শেয়ার পাঠানো void f(int&)
Pass by pointer Address of argument অ্যাড্রেস পাঠানো void f(int*)
Inline Compiler may substitute body কম্পাইলার বডি বসাতে পারে inline int sq(int)
Optimisation flag Compiler speed setting অপটিমাইজেশন স্তর -O2, -O3
std::endl Newline + flush নতুন লাইন + ফ্লাশ cout<<endl;

4. Deep Theory Explanation

4.1 Build cycle

hello.cpp ── preprocess ──▶ hello.i  (#include resolved)
           ── compile   ──▶ hello.s  (assembly)
           ── assemble  ──▶ hello.o  (object)
           ── link      ──▶ hello    (executable)

g++ does all four steps by default: g++ hello.cpp -o hello. To stop earlier: -E preprocess, -S assembly, -c object only.

বাংলায়: g++ এক কমান্ডে চারটা ধাপ চালায়: preprocessor (#include বসানো), compiler (C++ থেকে assembly), assembler (assembly থেকে machine code), আর linker (সব জোড়া লাগিয়ে executable)। -E, -S, -c flag দিয়ে যেকোনো ধাপে থেমে যাওয়া যায় — কোন flag কোন ফাইল (.i, .s, .o) বানায় সেটা পরীক্ষার নিশ্চিত প্রশ্ন, Chapter 12-তে এর পূর্ণ বিবরণ আছে।

Useful flags:

Flag Meaning
-O0/-O1/-O2/-O3 Optimisation level
-Ofast Beyond -O3, may break IEEE-754
-g Debug info
-Wall -Wextra -Wpedantic Warnings
-std=c++17 / -std=c++20 Language standard
-march=native Tune for current CPU
-fopenmp Enable OpenMP
-I<dir> Add include path
-L<dir> -lname Library paths

4.2 Variables, types, sizeof

(example02.cpp)

int   a = 34;       // 4 bytes
char  b = 'k';      // 1 byte
float c = 32.56;    // 4 bytes
std::cout << sizeof(int);   // → 4

4.3 Scope & namespaces

(example03.cpp, example04.cpp)

int a = 34;          // global

int main() {
    int a = 23;          // local — shadows global
    std::cout << a;       // 23
    std::cout << ::a;     // 34 — global qualifier
}

Namespaces:

namespace box {
    int a = 0;
}

int main() {
    int a = 23;
    box::a = 34;
    std::cout << a << " " << box::a;   // 23 34
}

std:: is the namespace of the C++ standard library; using namespace std; makes names unqualified (avoid in headers).

বাংলায়: Scope মানে একটা নাম কোথা থেকে দেখা যায়। Local variable একই নামের global-কে ঢেকে দেয় (shadowing); তখন ::a লিখে global-টা ধরতে হয়, আর box::a লিখে box namespace-এরটা। using namespace std লিখলে সব std:: নাম খালি হয়ে যায় — header ফাইলে এটা করা খারাপ অভ্যাস, কারণ নামের সংঘর্ষ হতে পারে। পরীক্ষায় shadowing-এর output predict (23 নাকি 34?) খুব প্রিয় প্রশ্ন।

4.4 Pointers

(example05.cpp)

int b = 23;
int *a = &b;          // a holds &b
std::cout << a;       // address (e.g. 0x7ffd...)
std::cout << *a;      // 23 — dereference

Three operators:

  • &x — address of x.
  • *p — value pointed to by p (as expression).
  • int *p — declaration of pointer to int.

Null pointer: int *p = nullptr;. Dereferencing it crashes the program.

Pointer diagram — two boxes in memory, the pointer's value is the other box's address:

        int b = 23;                        int *p = &b;

   address: 0x7ffc1000                address: 0x7ffc1008
   ┌─────────────────────┐            ┌─────────────────────┐
   │     b  =  23        │ ◄──────────│  p = 0x7ffc1000     │
   └─────────────────────┘  "points   └─────────────────────┘
                              to"
   &b  ─►  0x7ffc1000     (address-of operator)
    p  ─►  0x7ffc1000     (the value stored in p IS that address)
   *p  ─►  23             (dereference: follow the arrow, read the box)

   *p = 5;   writes 5 into b's box  ─►  b is now 5

বাংলায়: ছবিটা মাথায় গেঁথে নিন: b একটা বাক্স যাতে 23 আছে, আর p আরেকটা বাক্স যাতে b-র ঠিকানা লেখা আছে। &b মানে "b-র ঠিকানা দাও", p-এর ভেতরের মানটাই সেই ঠিকানা, আর p মানে "তীরচিহ্ন ধরে গিয়ে বাক্সটা খোলো"। p = 5 লিখলে আসলে b-ই বদলায় — কারণ দুজনে একই বাক্সের কথা বলছে। উদ্যোগহীন (uninitialised) pointer dereference করলেই segfault — পরীক্ষার প্রিয় bug।

4.5 Arrays and pointer arithmetic

(example06.cpp, example07.cpp)

int a[5] = {34,76,48,55,7};
for (int i=0; i<5; ++i)
    std::cout << a[i] << "\n";

int *b = &a[0];        // points at a[0]
for (int i=0; i<5; ++i)
    std::cout << (b+i) << " : " << *(b+i) << "\n";  // pointer + int = next int
  • a[i] is sugar for *(a+i).
  • Pointer arithmetic moves by sizeof(type) bytes per step.
  • Be careful not to read past a[4] — undefined behaviour.

বাংলায়: Array-র নামটা আসলে প্রথম element-এর ঠিকানায় "ক্ষয়" (decay) হয়ে যায়, তাই a[i] আর *(a+i) হুবহু একই জিনিস। Pointer-এ +1 করলে 1 byte নয়, sizeof(type) byte এগোয় — int-এ 4, double-এ 8। ঠিক কত byte এগোবে সেই হিসাবটা §4.11-এ hex ঠিকানা দিয়ে করা আছে; পরীক্ষায় ঠিকানা মেলাতে দেওয়া হয়।

4.6 Conditionals & loops

(example08.cpp)

for (int i=0; i<5; ++i) {
    if (a[i] % 2 == 0) {
        std::cout << "a[" << i << "] = " << a[i] << "\n";
    }
}

Other forms: if (...) {} else if (...) {} else {}, switch, while, do-while, for.

Ternary: cout << (n>0 ? "pos" : "non-pos");.

4.7 Functions

(example09.cpp)

bool is_prime(int num);          // declaration

bool is_prime(int num)           // definition
{
    if (num<2) return false;
    for (int i=2; i<num; ++i)
        if (num%i==0) return false;
    return true;
}

A declaration (prototype) tells the compiler the signature; a definition gives the body. You can put declarations in .h files and definitions in .cpp files.

4.8 Call by value / pointer / reference

(example10.cpp)

void callByValue(int a)        { a++; }       // copy → caller's var unchanged
void callByPointer(int* a)     { (*a)++; }    // modify via address
void callByReference(int& a)   { a++; }       // alias → modifies caller

Modern C++ prefers int&; pointers (int*) are needed when you may pass nullptr or arrays. Const-references (const T&) are the fast read-only idiom for big objects (CFD fields).

CALL BY VALUE:  void f(int a)           CALL BY REFERENCE:  void f(int& a)
──────────────────────────────────      ──────────────────────────────────
caller                                  caller
┌────────────────┐                      ┌────────────────┐
│  x = 34        │                      │  x = 34 ─► 35  │◄──────────┐
└───────┬────────┘                      └────────────────┘           │
        │ the VALUE 34 is COPIED                 ▲                   │
        ▼                                        │ alias: a IS x    │
callee f                                callee f │                   │
┌────────────────┐                      ┌────────┴──────────────────┐│
│  a = 34 ─► 35  │  separate box!       │ a has NO box of its own — ││
└────────────────┘                      │ it refers straight into   ├┘
  a++ changes only the copy             │ the caller's box          │
  copy destroyed at return              └───────────────────────────┘
  caller still sees x = 34                a++ ─► caller sees x = 35

বাংলায়: By value মানে callee নিজের আলাদা বাক্সে কপি পায় — সেখানে যা-ই করুক, caller-এর x অক্ষত থাকে আর return-এর সময় কপিটা মুছে যায়। By reference মানে a-র নিজের কোনো বাক্সই নেই, ওটা caller-এর x-এরই আরেক নাম — a++ করলেই x বদলে যায়। বড় object-এর জন্য const T& হলো সেরা সমাধান: কপিও হয় না, ভুলে বদলানোও যায় না। পরীক্ষায় example10.cpp-এর "34 নাকি 35" প্রশ্নটা এই ছবির ওপরেই দাঁড়িয়ে।

4.9 Classes and objects

(task_02_solution.cpp — Complex class, full)

class Complex {
private:
    float a;
    float b;
public:
    Complex(float x, float y) : a(x), b(y) {}    // constructor with init list
    void display();
    float real();
    float img();
    Complex add(Complex c1, Complex c2);
    Complex multiply(Complex c1, Complex c2);
    float modulus();
};

void Complex::display()  { std::cout << a << "+" << b << "i\n"; }
float Complex::real()    { return a; }
float Complex::img()     { return b; }

Complex Complex::add(Complex c1, Complex c2) {
    return Complex(c1.real()+c2.real(), c1.img()+c2.img());
}

Complex Complex::multiply(Complex c1, Complex c2) {
    float r = c1.real()*c2.real() - c1.img()*c2.img();
    float i = c1.real()*c2.img() + c1.img()*c2.real();
    return Complex(r, i);
}

float Complex::modulus() { return std::sqrt(a*a + b*b); }

Concepts:

  • class { private: …; public: …; }; — members default to private.
  • Constructor Complex(float, float) — no return type, automatically called when an object is created.
  • Member functions accessed via obj.method().
  • Complex::method definition syntax outside the class.

বাংলায়: Class মানে data আর function একসাথে বেঁধে রাখা: a, b হলো private (বাইরে থেকে ছোঁয়া যায় না), আর display, add-এর মতো method গুলো public। Constructor-এর নাম class-এর নামের সমান এবং কোনো return type নেই — void-ও নয়, এটা লিখলেই compile error। আর class-এর শেষের ; (semicolon) ভুলে গেলে পরের লাইনে অদ্ভুত error আসে। Complex class-টা পরীক্ষার সবচেয়ে দামি প্রশ্ন — পুরোটা মুখস্থ করে হাতে লিখে প্র্যাকটিস করুন।

4.10 Diagrams from V09

  • Memory layout diagram — int a; int *p=&a; showing two boxes with arrow.
  • Call-by-value vs call-by-reference table — caller's a vs callee's a.
  • Class diagram — Complex with private fields, public methods.

4.11 Memory mathematics — sizeof, pointer arithmetic, copy cost

The sizeof table (x86-64 Linux, LP64 model) — memorise:

Type sizeof (bytes) Notes
char 1 by definition
bool 1
int 4 32-bit two's complement
float 4 IEEE-754 single precision
long 8 on Linux x86-64
double 8 IEEE-754 double precision
any pointer T* 8 a 64-bit address — independent of T

Pointer arithmetic rule. For a pointer p of type T* and integer \(k\):

\[ \mathrm{address}(p + k) = \mathrm{address}(p) + k \cdot \mathrm{sizeof}(T) \]

Worked example 1 — int array. Let int a[5] start at address 0x7ffc2000. With \(\mathrm{sizeof(int)} = 4\):

Expression Computation Address
a + 0 \(\texttt{0x7ffc2000} + 0\cdot 4\) 0x7ffc2000
a + 1 \(\texttt{0x7ffc2000} + 1\cdot 4\) 0x7ffc2004
a + 2 \(\texttt{0x7ffc2000} + 2\cdot 4\) 0x7ffc2008
a + 4 \(\texttt{0x7ffc2000} + 4\cdot 4 = +16 = \texttt{0x10}\) 0x7ffc2010

Worked example 2 — double array. Let double d[5] start at 0x7ffc3000. With \(\mathrm{sizeof(double)} = 8\):

Expression Computation Address
d + 1 \(+1\cdot 8 = \texttt{0x8}\) 0x7ffc3008
d + 3 \(+3\cdot 8 = 24 = \texttt{0x18}\) 0x7ffc3018
d + 4 \(+4\cdot 8 = 32 = \texttt{0x20}\) 0x7ffc3020

Same +1, different byte step — that is the whole point of typed pointers. Pointer difference works backwards: (a+4) - a = 4 elements = \(4 \cdot 4 = 16\) bytes.

Copy cost: pass-by-value vs pass-by-reference. A struct holding \(N\) doubles occupies \(8N\) bytes. Passing it to a function costs:

\[ C_{\text{value}} = 8N \ \text{bytes copied}, \qquad C_{\text{ref}} = 8 \ \text{bytes (one address)} \]

Worked numbers for a CFD field with \(N = 10^6\) cells: by value, every call copies \(8 \cdot 10^6 = 8\,\mathrm{MB}\); at a memory bandwidth of \(10\,\mathrm{GB/s}\) that is \(8\cdot 10^6 / 10^{10} = 0.8\,\mathrm{ms}\) per call — inside a loop over \(10^4\) timesteps that is 8 seconds of pure copying. By reference the cost stays at 8 bytes, a ratio of \(10^6\). Hence the HPC idiom void solve(const Field& f).

Process memory layout — where everything lives:

 high addresses
┌────────────────────────────────┐  ~0x7fffffffffff
│             STACK              │  function frames & locals:
│  main() frame: int x; p; ...   │    int x;  double y[10];
│  f() frame: params, locals     │  every call PUSHES a frame,
│             │                  │  every return POPS it
│             ▼   grows DOWN     │  (size limit ~8 MB!)
├────────────────────────────────┤
│         (unused gap)           │
├────────────────────────────────┤
│             ▲   grows UP       │
│             │                  │
│             HEAP               │  new double[N] lives HERE;
│   new double[1000000] = 8 MB   │  survives until delete[];
│   block sits here; only the    │  the POINTER to it sits in a
│   pointer to it is on the stack│  stack frame
├────────────────────────────────┤
│          DATA + BSS            │  globals & statics:
│   int g = 34;     (data)       │  initialised  -> data
│   static int cnt; (bss, zeroed)│  uninitialised -> bss
├────────────────────────────────┤
│             TEXT               │  the machine code itself:
│   main, swap, Complex::add ... │  read-only
└────────────────────────────────┘  low addresses

বাংলায়: Stack হলো ছোট আর স্বয়ংক্রিয় — প্রতিটা function call-এ একটা frame জমে, return করলেই মুছে যায়; সাধারণ local variable এখানেই থাকে। Heap হলো বড় কিন্তু manual — new দিয়ে নিতে হয়, delete দিয়ে ফেরত দিতে হয়, না দিলে memory leak। মনে রাখুন: stack-এর সীমা মোটে ~8 MB, তাই কোটি cell-এর CFD field সবসময় heap-এ (new বা std::vector) রাখতে হয় — stack-এ নিলেই stack overflow।

4.12 Complex-number mathematics and the Complex class

For \(z_1 = a + bi\) and \(z_2 = c + di\) (with \(i^2 = -1\)):

\[ z_1 + z_2 = (a + c) + (b + d)i \]
\[ z_1 \cdot z_2 = (ac - bd) + (ad + bc)i \]
\[ |z| = \sqrt{a^2 + b^2}, \qquad \bar{z} = a - bi, \qquad z\bar{z} = a^2 + b^2 = |z|^2 \]

Worked example by hand: \((3+4i)\cdot(1-2i)\). Here \(a=3,\ b=4,\ c=1,\ d=-2\):

  • Real part: \(ac - bd = 3\cdot 1 - 4\cdot(-2) = 3 + 8 = 11\)
  • Imaginary part: \(ad + bc = 3\cdot(-2) + 4\cdot 1 = -6 + 4 = -2\)
  • Result: \(11 - 2i\).

Also: \((3+4i) + (1-2i) = 4 + 2i\); \(\ |3+4i| = \sqrt{9+16} = \sqrt{25} = 5\); \(\ \overline{3+4i} = 3-4i\) and \((3+4i)(3-4i) = 9 + 16 = 25 = |z|^2\).

The code that produces exactly this (complete, compilable Complex class with constructor, operator+, operator*, abs, conj, print):

#include <iostream>
#include <cmath>

class Complex {
private:
    double re, im;
public:
    Complex(double r = 0.0, double i = 0.0) : re(r), im(i) {}   // constructor

    Complex operator+(const Complex& o) const {                 // (a+c) + (b+d)i
        return Complex(re + o.re, im + o.im);
    }
    Complex operator*(const Complex& o) const {                 // (ac-bd) + (ad+bc)i
        return Complex(re * o.re - im * o.im,
                       re * o.im + im * o.re);
    }
    Complex conj() const { return Complex(re, -im); }           // a - bi
    double  abs()  const { return std::sqrt(re*re + im*im); }   // sqrt(a^2+b^2)

    void print() const {
        std::cout << re << (im >= 0 ? "+" : "") << im << "i\n";
    }
};

int main() {
    Complex z1(3.0, 4.0), z2(1.0, -2.0);
    Complex s = z1 + z2;     // 4+2i
    Complex p = z1 * z2;     // 11-2i
    s.print();
    p.print();
    std::cout << "abs(z1) = " << z1.abs() << "\n";   // 5
    z1.conj().print();                                // 3-4i
    return 0;
}

Compile and run: g++ -O2 -std=c++17 -Wall complex.cpp -o complex && ./complex

4+2i
11-2i
abs(z1) = 5
3-4i

বাংলায়: Complex গুণের সূত্রটা \((ac-bd) + (ad+bc)i\) মুখস্থ রাখুন — মাইনাসটা real part-এ, কারণ \(i\cdot i = -1\)। Modulus মানে মূলবিন্দু থেকে দূরত্ব: \(\sqrt{a^{2}+b^{2}}\), আর conjugate মানে শুধু imaginary অংশের চিহ্ন উল্টে দেওয়া। হাতে হিসাব করে তারপর কোড মিলিয়ে দেখুন — পরীক্ষায় দুটোই চাওয়া হয়: হাতের হিসাব এবং operator overload — দুটোই প্রস্তুত রাখুন।

4.13 Pointer & call diagrams

(a) Pointer ↔ variable:

        int a = 5;            int *p = &a;
   ┌──────────────┐        ┌──────────────┐
   │  a            │        │  p           │
   │  value: 5     │◄───────┤ value:0x7ffc │   p stores a's ADDRESS
   │  addr: 0x7ffc │        │ addr: 0x7ff4 │
   └──────────────┘        └──────────────┘
   *p  reads/writes the box p points to:  *p = 7  ⇒  a == 7
   &a  asks for a's address:              &a == 0x7ffc

(b) Pass-by-value vs pass-by-reference:

   void f(int x)  — BY VALUE          void g(int& x) — BY REFERENCE
   caller: a=5                        caller: a=5
   ┌────────┐   copy   ┌────────┐     ┌────────┐  alias   ┌────────┐
   │ a = 5  │ ───────► │ x = 5  │     │ a = 5  │◄────────►│   x    │
   └────────┘          └────────┘     └────────┘          └────────┘
   x++ → x=6, a STILL 5               x++ → a == 6  (same box!)
   copy dies at return                no copy, no extra memory

বাংলায়: ছবি দুটো মাথায় গেঁথে নাও: pointer হলো আলাদা একটা বাক্স যাতে অন্য বাক্সের ঠিকানা লেখা; reference হলো একই বাক্সের দ্বিতীয় নাম। value-তে পাঠালে ফটোকপি যায় — আসল কাগজ অক্ষত; reference/pointer-এ পাঠালে আসল কাগজটাই যায়। CFD-তে কোটি-cell-এর array ফটোকপি করা অসম্ভব — তাই সব বড় জিনিস const T& বা pointer দিয়ে যায়।


4.14 Object-oriented programming — the four principles (new 2026 slides)

The 2026 deck adds explicit OOP theory. Learn the four principles WITH a C++ example each:

Principle One-line definition C++ mechanism
Encapsulation Bundle data + methods; protect state behind access control private: data, public: methods
Abstraction Hide implementation details, show only the essential interface abstract class = has virtual … = 0; (pure virtual)
Inheritance A class reuses/extends another ("is-a") class Wall : public BoundaryCondition { … };
Polymorphism One interface, many behaviours chosen at runtime virtual functions called through a base pointer/reference
class BoundaryCondition {                 // ABSTRACTION: interface only
public:
    virtual void apply(Field& f) const = 0;   // pure virtual
    virtual ~BoundaryCondition() = default;
};

class Wall : public BoundaryCondition {   // INHERITANCE
public:
    void apply(Field& f) const override { /* no-slip */ }   // POLYMORPHISM
};

class Inlet : public BoundaryCondition {
    double u_in;                          // ENCAPSULATION: private state
public:
    explicit Inlet(double u) : u_in(u) {}
    void apply(Field& f) const override { /* set u_in */ }
};

// runtime dispatch — the CFD pay-off:
void applyAll(const std::vector<std::unique_ptr<BoundaryCondition>>& bcs, Field& f){
    for (const auto& bc : bcs) bc->apply(f);   // each object picks ITS apply()
}

Extra 2026 slide facts worth quoting: C++ is a middle-level language (systems access + high-level abstraction); multiple and hybrid inheritance are allowed in C++ (unlike Java) — a class can inherit from several bases; an abstract class (any pure virtual member) cannot be instantiated and defines an interface.

Exam phrasing to be ready for: "Name and explain the four principles of OOP", "Describe the intention of class structure, access protection, and abstract classes", "Give a short example of a class structure."

বাংলায়: OOP-র চার স্তম্ভ এক লাইনে করে: Encapsulation = ডেটা লুকিয়ে method দিয়ে ঘেরা (private/public); Abstraction = কেবল দরকারি interface দেখানো (pure virtual = 0); Inheritance = "is-a" সম্পর্কে কোড পুনর্ব্যবহার; Polymorphism = base pointer দিয়ে ডাকলে যার যার নিজের version চলে (virtual)। CFD-সংযোগটা বলো: সব BoundaryCondition এক interface, প্রতিটা type নিজের apply() চালায় — এটাই polymorphism-এর আসল লাভ। আর মনে রাখো: C++-এ multiple inheritance বৈধ — এটা ২০২৬-এর নতুন স্লাইডের প্রিয় লাইন।

5. Command / Syntax / Code Breakdown

g++ src.cpp -o exe

Compile + link to exe. Use -O2 -Wall -std=c++17 in production.

#include <iostream>

Bring in the iostream header so std::cout works.

int main() { … return 0; }

Program entry point. 0 indicates success.

int a = 5;

Variable declaration + init.

int& r = a;

Reference declaration. Must be initialised.

int *p = &a;

Pointer declaration + init. &a = address of a.

*p = 7;

Write through pointer.

for (int i=0; i<n; ++i) { … }

C-style loop.

class C { public: int x; };

Trivial class.

void f(int); then void f(int x){…}

Declaration + definition.

Complex c(3,4);

Construct an object via constructor.


6. Mandatory Practical Examples

Example 6.1 — Hello world (example01.cpp)

Expected output: Hello World

  • #include <iostream> brings in the IO library.
  • int main() mandatory entry.
  • std::cout << "Hello World" << std::endl; print + newline + flush.
  • return 0; exit success.

Example 6.2 — Sizes (example02.cpp)

Size of int : 4
Size of char : 1
Size of float : 4

(Note: the lecture's example03 prints sizeof(c) (a float) under "Size of int" — typo. Exam-tip: mention this.)

Example 6.3 — Local vs global, scope resolution (example03.cpp)

a = 23
a = 34

::a accesses the global.

Example 6.4 — Namespace (example04.cpp)

a = 23
a = 34

Example 6.5 — Pointers (example05.cpp)

address(b) = 0x7ffe...            ← actual address varies
value(address(b)) = 23

Example 6.6 — Array iteration (example06.cpp)

a[0] = 34
a[1] = 76
a[2] = 48
a[3] = 55
a[4] = 7

Example 6.7 — Pointer arithmetic over array (example07.cpp)

0x7ffe...0 : 34
0x7ffe...4 : 76
0x7ffe...8 : 48
0x7ffe...c : 55
0x7ffe...10 : 7

(Each address is +4 bytes apart since int is 4 bytes.)

Example 6.8 — Conditional filter (example08.cpp)

a[0] = 34
a[2] = 48

(Only even values printed.)

Example 6.9 — Function: is_prime (example09.cpp)

a[1] = 2
a[2] = 3
a[4] = 5
a[6] = 7

Example 6.10 — Call by value vs reference (example10.cpp)

a after call by value:34
a after call by reference:35

By value: copy is incremented locally and discarded. By reference: original is modified.

Example 6.11 — Swap (E09 Task 1, solution)

#include <iostream>
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main() {
    int a=34, b=25;
    std::cout << "Before: a=" << a << " b=" << b << "\n";
    swap(&a, &b);
    std::cout << "After: a=" << a << " b=" << b << "\n";
    return 0;
}

Output:

Before: a=34 b=25
After: a=25 b=34

Real-Life HPC/CFD Meaning. Swap-by-pointer demonstrates how solvers pass fields by address — never copy multi-MB arrays.

Written Exam Relevance. "Implement swap" appears in nearly every C++ basics exam.

Example 6.12 — Complex class (E09 Task 2, full solution)

#include <iostream>
#include <cmath>

class Complex {
private:
    float a, b;
public:
    Complex(float x, float y) : a(x), b(y) {}
    void display() { std::cout << a << "+" << b << "i\n"; }
    float real()     { return a; }
    float img()      { return b; }
    Complex add(Complex c1, Complex c2) {
        return Complex(c1.real()+c2.real(), c1.img()+c2.img());
    }
    Complex multiply(Complex c1, Complex c2) {
        return Complex(c1.real()*c2.real() - c1.img()*c2.img(),
                       c1.real()*c2.img() + c1.img()*c2.real());
    }
    float modulus() { return std::sqrt(a*a + b*b); }
};

int main() {
    Complex c1(3,4), c2(4,5), c3(0,0);
    c1.display();
    c2.display();
    c3 = c3.add(c1,c2);      c3.display();          // 7+9i
    c3 = c3.multiply(c1,c2); c3.display();          // -8+31i
    std::cout << "Modulus of c3 = " << c3.modulus() << "\n"; // sqrt(64+961) ≈ 32.02
    return 0;
}

Expected output:

3+4i
4+5i
7+9i
-8+31i
Modulus of c3 = 32.0156

Check the multiplication by hand: \((3+4i)(4+5i) = (3\cdot4 - 4\cdot5) + (3\cdot5 + 4\cdot4)i = -8 + 31i\). Modulus: \(\sqrt{(-8)^2+31^2} = \sqrt{64+961} = \sqrt{1025} \approx 32.0156\).

Real-Life Meaning. The same OO pattern is used in CFD for Vector, Tensor, Field, BoundaryCondition classes.

Written Exam Relevance. Top-mark question often: "Complete the class so that c3 = c3.add(c1,c2) works." Provide the constructor + member function definitions.


7. Real HPC/CFD Workflow

# 1. Edit
vim solver.cpp

# 2. Compile fast
g++ -O3 -march=native -std=c++17 -fopenmp solver.cpp -o solver

# 3. Run (single-thread)
./solver in.dat > out.log

# 4. Run (8 OpenMP threads)
OMP_NUM_THREADS=8 ./solver in.dat > out.log

# 5. Profile
g++ -O2 -g -pg solver.cpp -o solver_prof
./solver_prof in.dat
gprof solver_prof gmon.out | head

# 6. Debug (Ch.13)
g++ -O0 -g solver.cpp -o solver_dbg
gdb ./solver_dbg

8. Exercises and Solutions

E09 Task 1 (swap) — see 6.11.

Marking scheme (8 marks):

  • 1 declaration void swap(int*, int*).
  • 2 use of *a, *b to read/write.
  • 1 temporary variable.
  • 2 main: call as swap(&a,&b).
  • 2 correct output before/after.

Common mistake. Writing void swap(int a, int b){ int t=a; a=b; b=t; } (call-by-value) — does nothing.

Harder version. Use references: void swap(int& a, int& b){ int t=a; a=b; b=t; } and call swap(a,b).

E09 Task 2 (Complex class) — see 6.12.

Marking scheme (12 marks):

  • 1 private: data members.
  • 2 constructor.
  • 1 display.
  • 1 real/img getters.
  • 2 add correctness.
  • 2 multiply correctness.
  • 1 modulus formula.
  • 1 main: construct + invoke.
  • 1 expected output.

Common mistakes.

  • Forgot ; after class definition.
  • Constructor with void return type → compile error (constructors have no return type).
  • Used pow(a,2)+pow(b,2) instead of a*a+b*b — works but slower in HPC; prefer multiplication.
  • class default access is private; forgot public: → main can't call methods.

Harder version. Add an operator+ / operator* overload so c3 = c1 + c2. Use a const-ref signature to avoid copies:

Complex operator+(const Complex& other) const {
    return Complex(a + other.a, b + other.b);
}

বাংলায়: operator overloading-এর তিনটা exam-checkpoint: (১) parameter const Complex& — copy এড়াতে, (২) ফাংশনের শেষে const — নিজের object বদলায় না বোঝাতে, (৩) নতুন object return। এই তিনটা লিখলেই "harder version" -এর পুরো নম্বর।


9. Written Exam Focus

9.1 Short Answers

Q. Difference between pointer and reference. A. A pointer holds an address — can be null, can be re-assigned, requires * to dereference. A reference is an alias to an existing variable — cannot be null, cannot be re-bound, used like the original.

Q. What does & do in C++? A. As a unary operator on a value (&x), it returns the address. In a type declaration (int& r), it indicates a reference type.

Q. Why use call-by-reference? A. Avoids copying the argument and lets the function modify the caller's variable. Crucial for large CFD fields.

Q. Difference between class declaration and definition. A. A declaration introduces the name (class Complex;); a definition provides the full body (class Complex { … };).

Q. What is std::endl? A. Inserts '\n' AND flushes the stream. For performance use '\n' alone.

9.2 Medium Answers

Q. (8 marks) Explain pointers and arrays in C++ with code.

A. A pointer (int *p) stores a memory address. &x yields the address of x; *p accesses the value at the address. An array int a[5] is contiguous memory; the name decays to a pointer to its first element. Pointer arithmetic *(a+i) is equivalent to a[i]. Example:

int a[5]={1,2,3,4,5};
int* b=&a[0];
for(int i=0;i<5;++i) std::cout<<*(b+i)<<" ";

Common mistake: dereferencing past the end → undefined behaviour.

Q. (5 marks) Compare call by value vs reference vs pointer.

A. Value: a copy is made; original untouched (void f(int)). Reference: alias to original; modifications propagate (void f(int&)). Pointer: caller passes the address (f(&x)), callee modifies via *p (void f(int*)). References are cleanest; pointers are needed when nullability or array passing is required.

9.3 Long Answer (12 marks)

Q. Walk through the Complex class implementation.

A.

Introduction. The Complex class encapsulates a complex number with real (a) and imaginary (b) parts and provides operations.

Main concept. OO encapsulation: data is private, methods are public. A constructor Complex(float, float) initialises the state.

Step-by-step.

  1. Headers <iostream>, <cmath> for std::sqrt.
  2. class Complex { private: float a,b; public: … };
  3. Constructor with member-init list: Complex(float x,float y) : a(x), b(y) {}.
  4. Member functions defined outside with Complex::name.
  5. add: \((c_1.a+c_2.a) + (c_1.b+c_2.b)i\).
  6. multiply: \((ac-bd) + (ad+bc)i\).
  7. modulus: \(\sqrt{a^2+b^2}\).

Example. \((3+4i)(4+5i) = (12-20) + (15+16)i = -8+31i\).

Real HPC/CFD link. The same OO pattern wraps Vector, Tensor, Field, BoundaryCondition in OpenFOAM. Composition + inheritance = the C++ way to model physics.

Conclusion. Classes provide reusable, type-safe abstractions central to HPC code.

9.4 Output Prediction

int a=10, *p=&a;
*p = 20;
std::cout << a;           // 20
int a=10; int& r=a; r=30; std::cout<<a;        // 30

example10.cpp predicted output:

a after call by value:34
a after call by reference:35

9.5 Comparison

Pointer Reference
Syntax int *p; *p int& r; r
Reseat yes no
Null yes no
Indirection explicit implicit
Use nullable, array, dynamic safer alias
class struct
Default access private public
Used for rich objects POD

9.6 Templates

Compile template: g++ -O2 -std=c++17 -Wall -fopenmp file.cpp -o exe; ./exe

Pointer template: int x=5; int *p=&x; *p=7; cout<<x; → 7.

Class template:

class T {
    private: /* data */;
    public:
        T(args) : /* init list */ {}
        ret method(args);
};
ret T::method(args) { /* ... */ }

Swap-by-pointer template: see 6.11.

9.7 Marking Scheme — "Complex class with operator+" (10 marks)

  • 2 class skeleton (private/public).
  • 2 constructor.
  • 1 display.
  • 2 add/multiply/modulus.
  • 2 operator+ overload (const Complex&).
  • 1 expected output.

10. Very Hard Questions

Beginner

  1. Compile hello.cpp. → g++ hello.cpp -o hello.
  2. Print "hi". → std::cout<<"hi"<<std::endl;
  3. Declare an int. → int a=5;
  4. Address of a. → &a.
  5. Pointer to a. → int *p=&a;

Intermediate

  1. Loop over array length 5. → for(int i=0;i<5;++i) ...
  2. Pass int by reference. → void f(int& a).
  3. Class with public method display(). → see Complex.
  4. Constructor with init list. → T():a(0){}.
  5. Why is using namespace std bad in headers? → name pollution.

Hard

  1. Why does f(int) not modify the caller's variable? → call-by-value copies.
  2. What's wrong with int *p; *p=5;? → uninitialised pointer (UB/segfault).
  3. Difference between delete and delete[]. → single object vs array.
  4. Why is endl slow? → flushes the buffer.
  5. Write a function returning an object of class C. → return by value uses copy/move.

Very Hard

  1. Why do CFD solvers prefer const Field& over Field? → avoid expensive copies.
  2. Difference between stack and heap allocation. → automatic vs new/delete, lifetime.
  3. What happens if you forget ; after class? → cascading errors at the next declaration.

Deep Integration

  1. How does pointer arithmetic underpin SIMD vectorisation? → contiguous memory, stride 1.
  2. Compare RAII to manual new/delete. → constructor acquires, destructor releases — exception-safe.

Coding/Command

  1. Implement swap(int& a, int& b). → see harder version in §8.
  2. Add operator+ to Complex. → see 9.7.

Debugging

  1. *p=10; placed before int a; p=&a; — what happens? → p is dereferenced uninitialised; segfault/UB.
  2. class C{int x;}; C c; c.x=5; — error? → x is private by default; need public: or an accessor.

Long Written

  1. (250 words) Discuss why HPC kernels avoid copying data — call-by-reference vs pointer trade-offs.

11. Debugging and Mistake Analysis

Mistake Why wrong Correct Explanation
if(a=5) assignment, not equality if(a==5) classic
int *p; *p=5; uninitialised int a; int *p=&a; *p=5; dereference safe pointer
swap(a,b) by value no-op swap(&a,&b) (ptr) or int& version reference types
Forgot ; after class cascade errors class X { … }; semicolon
private: accessed outside error accessor or public: encapsulation
Mismatched new/delete leak / UB delete[] for arrays RAII / smart pointers
using namespace std in .h symbol clashes qualify std:: hygiene
cout << endl in tight loop slow flushes '\n' performance
int vs unsigned compare warnings/UB-ish bugs be consistent -Wsign-compare
Forgot return 0 UB pre-C++11 always return habit

বাংলায়: পরীক্ষায় "find the bug" এলে আগে এই চারটা খোঁজো: if(a=5) (একটা =), uninitialised pointer, value-passed swap, আর class-এর পরে ; নেই। এই চারটাই সব exam-এর ৮০% bug।


12. Mini Project for Mastery

Goal: Implement a vector-of-Complex with sum and modulus.

#include <iostream>
#include <vector>
#include <cmath>

class Complex {
private:
    float a, b;
public:
    Complex(float x=0, float y=0) : a(x), b(y) {}
    float real() const { return a; }
    float img() const { return b; }
    float modulus() const { return std::sqrt(a*a+b*b); }
    Complex operator+(const Complex& o) const { return {a+o.a, b+o.b}; }
    friend std::ostream& operator<<(std::ostream& os, const Complex& c){
        return os << c.a << (c.b>=0?"+":"") << c.b << "i";
    }
};

int main() {
    std::vector<Complex> v = { {1,2}, {3,4}, {5,6} };
    Complex sum;
    for (const auto& c : v) sum = sum + c;
    std::cout << "sum = "     << sum << "\n";
    std::cout << "modulus = " << sum.modulus() << "\n";
}

Compile: g++ -O2 -std=c++17 mini.cpp -o mini && ./mini

Output:

sum = 9+12i
modulus = 15

Hand-check: \((1+3+5) + (2+4+6)i = 9+12i\), \(|9+12i| = \sqrt{81+144} = \sqrt{225} = 15\).

Connection to exam: class, constructor with defaults, const-correctness, operator overload, friend, range-based for — all top-mark idioms.


13. Final Chapter Cheat Sheet

Item Memorise
Compile g++ -O2 -std=c++17 -Wall src.cpp -o exe
Print std::cout << x << '\n';
Read std::cin >> x;
Types int 4B, char 1B, float 4B, double 8B, bool 1B, ptr 8B
Pointer int *p = &a; *p = 7;
Reference int& r = a; r = 7;
Array int a[5]={…}; a[i] == *(a+i)
Pointer arithmetic p+1 advances by sizeof(T) bytes
Function decl/def ret name(args); / ret name(args){…}
Pass by val/ref/ptr f(int), f(int&), f(int*)
Class class C{ private:…; public:…; };
Constructor C(args):member(args){}
Member def ret C::method(args){…}
Modulus \(\sqrt{a^2+b^2}\)
Complex add \((a+c)+(b+d)i\)
Complex mul \((ac-bd)+(ad+bc)i\)
Optimisation -O2 -march=native
Debug -O0 -g, gdb
Trap if(a=5) (assignment)
Top phrase "References are aliases, pointers are addresses; both avoid copying — vital for HPC."

14. Mock Exam — Four Levels

Level 1 — Basic (definitions & syntax)

Q1. Declare a pointer to double and make it point to variable t.

Solution: double *p = &t;

Q2. What is printed? int a=3; int& r=a; r=8; std::cout<<a;

Solution: 8 — r is an alias for a.

Q3. Give the g++ command compiling main.cpp with warnings and C++17.

Solution: g++ -std=c++17 -Wall main.cpp -o main

Q4. What are the sizes of char, int, double, and a pointer on x86-64?

Solution: 1, 4, 8, 8 bytes.

Q5. Write the signature of a function norm that takes a Complex by const reference and returns float.

Solution: float norm(const Complex& c);

Level 2 — Intuitive (predict the output / explain why)

Q1. Predict:

int a[4] = {10,20,30,40};
int *p = a;
std::cout << *(p+2) << " " << p[1];

Solution: 30 20 — *(p+2) ≡ a[2]; p[1] ≡ a[1]. Identical notations.

Q2. Why does this NOT compile? int& r;

Solution: References must be bound at initialisation — there is no "null reference".

Q3. Predict:

void f(int x){ x=99; }
int main(){ int a=1; f(a); std::cout<<a; }

Solution: 1 — f gets a copy; the 99 dies with the copy.

Q4. double* q = new double[1000]; — where do q and the 1000 doubles live?

Solution: q itself is a local variable on the STACK; the 8000-byte block lives on the HEAP until delete[] q.

Q5. For int a[5] at address 0x1000, what is the address of a[3]? Show the math.

Solution: \(0x1000 + 3 \times 4 = 0x100C\) — pointer arithmetic scales by sizeof(int)=4.

Level 3 — Hard (exam level)

Q1. (8 marks) Compute \((2-3i)(1+4i)\) by hand using the multiplication formula, then write the two lines of C++ using the Complex class of 6.12 that compute and print it.

Solution: \((ac-bd) = 2\cdot1-(-3)(4) = 2+12 = 14\); \((ad+bc) = 2\cdot4+(-3)(1) = 8-3 = 5\) → \(14+5i\).

Complex z = c3.multiply(Complex(2,-3), Complex(1,4));
z.display();    // 14+5i
বাংলা ইঙ্গিত: মাইনাস-চিহ্নগুলো সাবধানে: \(-bd = -(-3)(4) = +12\) — sign-এর ভুলেই বেশিরভাগ নম্বর যায়।

Q2. (8 marks) This compiles but crashes. Find the bug and fix it:

int* make_array(int n){
    int arr[n];
    for(int i=0;i<n;++i) arr[i]=i*i;
    return arr;
}

Solution: arr is a stack array that dies when the function returns — the returned pointer dangles. Fix: heap-allocate int* arr = new int[n]; (caller must delete[]), or better return std::vector<int>. বাংলা ইঙ্গিত: function-এর ভেতরের local array return করা মানে ভাড়া-বাড়ির চাবি ফেরত দিয়ে ঠিকানা বিলি করা — stack frame শেষ, মেমরিও শেষ।

Q3. (10 marks) Write a function scale(double* v, int n, double k) that multiplies an array in place, and explain why it must take a pointer (or reference) rather than by value. Then show the call for double f[3]={1,2,3} with k=2 and the resulting array.

Solution:

void scale(double* v, int n, double k){
    for(int i=0;i<n;++i) v[i] *= k;
}
scale(f, 3, 2.0);   // f becomes {2,4,6}
Arrays decay to pointers; by-value copying is impossible for raw arrays and would anyway modify only a copy. The pointer gives direct access to the caller's memory. বাংলা ইঙ্গিত: array নাম নিজেই first element-এর pointer হয়ে যায় (decay) — তাই scale(f, …) লেখা যায়, &f[0] লিখতে হয় না।

Q4. (10 marks) Add to the Complex class: Complex conj() const and bool operator==(const Complex&) const. Write both, with one line of test code each.

Solution:

Complex conj() const { return Complex(a, -b); }
bool operator==(const Complex& o) const { return a==o.a && b==o.b; }
// tests:
Complex(3,4).conj().display();              // 3-4i
std::cout << (Complex(1,2)==Complex(1,2));  // 1
(Float equality is acceptable here; mention an epsilon-compare std::fabs(a-o.a)<1e-6 for bonus.) বাংলা ইঙ্গিত: getter/conj-জাতীয় "পড়া-শুধু" method-এর শেষে const — না দিলে const object থেকে ডাকা যায় না; এই খুঁটিনাটিই top marks আলাদা করে।

Q5. (10 marks) Predict the exact output and justify each line:

int x = 5;
int* p = &x;
int& r = x;
*p = *p + 1;
r = r * 2;
std::cout << x << " " << *p << " " << r << "\n";

Solution: 12 12 12 — all three names refer to ONE box: *p+1 → x=6; r*2 → x=12; printing x, p, r reads the same 12. বাংলা ইঙ্গিত:* pointer আর reference দুটোই থাকলে মাথায় একটাই ছবি আঁকো — বাক্স একটা, নাম তিনটা।

Level 4 — Beyond the lecture (transfer + coding)

Q1. Implement a minimal RAII wrapper DynArray holding double* with constructor (allocates n), destructor (frees), operator[], and a deleted copy constructor. Explain why copying must be forbidden (or deep-copied).

Solution:

class DynArray {
    double* data; int n;
public:
    explicit DynArray(int n_) : data(new double[n_]), n(n_) {}
    ~DynArray() { delete[] data; }
    DynArray(const DynArray&) = delete;            // no shallow copies!
    DynArray& operator=(const DynArray&) = delete;
    double& operator[](int i) { return data[i]; }
    int size() const { return n; }
};
A default (shallow) copy would duplicate the pointer; both objects would delete[] the same block → double free. Either delete copying or implement a deep copy. বাংলা ইঙ্গিত: "rule of three"-র মর্ম: destructor-এ delete থাকলে copy constructor/assignment-ও সামলাতে হবে — নাহলে double free।

Q2. An OpenMP loop (Ch 14 preview) over std::vector<double> v computes a sum. Why does for(auto x : v) sum += x; copy each element, and what is the zero-copy form? Write the OpenMP version.

Solution: auto x deduces a value type → element copy each iteration. Zero-copy: for (const auto& x : v). OpenMP:

double sum = 0.0;
#pragma omp parallel for reduction(+:sum)
for (size_t i = 0; i < v.size(); ++i) sum += v[i];
বাংলা ইঙ্গিত: range-for-এ & না দিলে প্রতি iteration-এ কপি — ছোট double-এ সস্তা, কিন্তু বড় class-এ মারাত্মক; অভ্যাসটা const auto&।

Q3. Memory math: a CFD field stores 3 velocity components + pressure as doubles for \(10^8\) cells. Compute the memory in GiB, and state whether passing this Field by value 4 times during one time step is feasible on a 64 GiB node.

Solution: \(4 \times 8\,\text{B} \times 10^8 = 3.2\times10^9\) B \(= 3.2/1.0737 \approx 2.98\) GiB per copy. Four by-value copies = ~11.9 GiB extra traffic + peak allocation — wasteful and possibly fatal alongside solver workspace; pass by const Field& (0 copies). বাংলা ইঙ্গিত: GiB-এ ভাগ \(2^{30} = 1.0737\times10^9\) দিয়ে — আর সিদ্ধান্তের যুক্তি সবসময় "কপি × সাইজ বনাম RAM"।

Q4. Combine with bash (Ch 8): write a script-friendly main that reads n from argv[1], fills an array with squares, prints the sum, and returns 1 on bad input — then the one-line bash test that checks the program fails on "abc".

Solution:

#include <iostream>
#include <cstdlib>
int main(int argc, char** argv){
    if (argc < 2) { std::cerr << "usage: prog n\n"; return 1; }
    char* end;
    long n = std::strtol(argv[1], &end, 10);
    if (*end != '\0' || n <= 0) { std::cerr << "bad n\n"; return 1; }
    long long sum = 0;
    for (long i = 1; i <= n; ++i) sum += i*i;
    std::cout << sum << "\n";
    return 0;
}
Bash test: ./prog abc >/dev/null 2>&1 && echo BAD || echo "correctly failed" বাংলা ইঙ্গিত: atoi চুপচাপ 0 দেয় — validation চাইলে strtol-এর end-pointer দেখতেই হবে; আর exit code-ই bash-এর সাথে C++-এর চুক্তিপত্র।


15. MCQ Drill — Exam Section (36 questions)

Difficulty ramps from basic (1) to very hard (36). Questions marked (select all that apply) have more than one correct option.

1. Compile main.cpp into an executable named main: A. gcc main B. g++ main.cpp -o main C. g++ -c main.cpp D. run main.cpp

Answer: B. -c would stop at the object file (no executable).

2. Production compile flags from the course: A. -O0 -g B. -O2 -std=c++17 -Wall C. -fast D. -debug

Answer: B. Optimise, pin the standard, enable warnings. (-O0 -g is the DEBUG combo.)

3. Sizes on x86-64: char / int / double / pointer = A. 1/2/4/4 B. 1/4/8/8 C. 2/4/8/16 D. 1/4/4/8

Answer: B. The sizeof table behind every memory-math question.

4. int *p = &a; — the & here means… A. reference type B. address-of a C. bitwise and D. logical and

Answer: B. In a TYPE (int&) it declares a reference; on a VALUE (&a) it takes the address.

5. *p = 7; does… A. multiplies p B. writes 7 into the object p points to C. moves the pointer D. declares p

Answer: B. Dereference-write: a becomes 7.

6. int& r = a; r = 30; — now a is… A. unchanged B. 30 — r is an alias for a C. undefined D. a copy

Answer: B. One box, two names.

7. Which are TRUE about references vs pointers? (select all that apply) A. a reference must be initialised B. a pointer can be null C. a reference can be re-seated later D. a pointer can be re-assigned

Answer: A, B, D. References bind once, forever — C is the false one.

8. a[i] is exactly equivalent to… A. (a+i) B. a+i C. &a[i] D. (i)a

Answer: A. Array indexing IS pointer arithmetic.

9. For int a[5] at address 0x1000, &a[3] is… A. 0x1003 B. 0x100C — three int-strides of 4 bytes C. 0x1030 D. 0x1004

Answer: B. p+k advances k·sizeof(T) bytes.

10. void f(int x) receives… A. the original variable B. a COPY — modifications vanish at return C. an address D. a reference

Answer: B. Call-by-value; the classic broken swap.

11. swap(a, b) genuinely swaps when its signature is… (select all that apply) A. void swap(int, int) B. void swap(int, int) called as swap(&a,&b) C. void swap(int&, int&) called as swap(a,b) D. void swap(const int&, const int&)

Answer: B, C. Value copies (A) and const refs (D) cannot modify the callers' variables.

12. Where do these live: int x in main / new double[N] / the function frame? A. heap/stack/heap B. stack / heap / stack C. all heap D. all stack

Answer: B. Locals and frames on the stack; new allocates on the heap until delete.

13. int* make(){ int arr[64]; return arr; } is broken because… A. 64 is too big B. arr is a STACK array — the pointer dangles the moment the function returns C. missing const D. syntax

Answer: B. Return std::vector or heap-allocate (and delete) instead.

14. delete p vs delete[] p: A. interchangeable B. single object vs array form — mismatching them is undefined behaviour C. delete[] is faster D. both free stacks

Answer: B. new ↔ delete, new[] ↔ delete[] — always paired.

15. A memory LEAK is… A. reading past an array B. losing the last pointer to a heap block without freeing it C. stack overflow D. a segfault

Answer: B. Fatal in long CFD runs: +1 MB per timestep → OOM kill (Ch 13 detects with valgrind).

16. class vs struct default access: A. both public B. class private, struct public C. class public, struct private D. both private

Answer: B. The only difference between the keywords.

17. A constructor… A. returns int B. has the class's name and NO return type; initialises the object C. is optional syntax for functions D. runs at delete

Answer: B. Complex(float x, float y) : a(x), b(y) {} — member-init list preferred.

18. c.x = 5; fails with "x is private". Correct reactions? (select all that apply) A. add a public setter/getter B. make x public if it's truly part of the interface C. friend-declare main D. it cannot be fixed

Answer: A, B. (C compiles but breaks encapsulation — mention, don't recommend.)

19. Forgetting the ; after class C { … } causes… A. nothing B. cascading, confusing errors at the NEXT declaration C. a linker error D. runtime crash

Answer: B. The most famous C++ beginner error message generator.

20. Complex multiplication (a+bi)(c+di) = A. (ac+bd) + (ad+bc)i B. (ac−bd) + (ad+bc)i C. (ac−bd) − (ad+bc)i D. ac + bdi

Answer: B. The minus lives in the REAL part (i²=−1). (3+4i)(4+5i) = −8+31i.

21. |3+4i| = A. 7 B. 5 C. 25 D. 3.5

Answer: B. √(9+16). Prefer aa+bb over pow(a,2) in HPC code.

22. Complex operator+(const Complex& o) const — the TRAILING const means… A. o is const B. the method does not modify *this — callable on const objects C. return is const D. compile-time only

Answer: B. The parameter's const& avoids a copy; the trailing const is a promise about the object.

23. Why const Complex& parameters instead of Complex? A. style B. avoids copying the argument while forbidding modification — free for big objects C. required by operator+ D. slower but safer

Answer: B. The Ch 11 performance mantra scaled up to Fields in real solvers.

24. std::endl vs '\n': A. identical B. endl also FLUSHES the stream — slow inside tight loops C. '\n' flushes D. endl is Windows-only

Answer: B. Use '\n'; flush only when you need it.

25. if (a = 5) is… A. comparison B. ASSIGNMENT (always true here) — the classic bug; write a == 5 C. error D. undefined

Answer: B. Compilers warn; -Wall makes the warning visible — another reason for Q2's flags.

26. The four OOP principles (2026 slides) are… A. speed, safety, style, syntax B. encapsulation, abstraction, inheritance, polymorphism C. classes, objects, methods, fields D. public, private, protected, friend

Answer: B. Each needs a one-liner + C++ mechanism in the written exam.

27. An ABSTRACT class in C++… A. has no members B. declares at least one pure virtual function (= 0) and cannot be instantiated — it defines an interface C. is marked abstract D. is final

Answer: B. virtual void apply(Field&) const = 0;.

28. Polymorphism through a base pointer requires… A. templates B. virtual functions — the call dispatches to the ACTUAL object's override at runtime C. friend D. static

Answer: B. for (auto& bc : bcs) bc->apply(f); — each BC type applies itself.

29. Which inheritance forms does C++ ALLOW? (select all that apply) A. single B. multiple C. hybrid D. none — like Java

Answer: A, B, C. "Multiple and hybrid inheritance are allowed in C++" — the 2026 slide line verbatim.

30. A base class with virtual functions should declare… A. nothing special B. a virtual destructor — deleting a Derived through a Base* must run the right destructor C. static destructor D. private destructor always

Answer: B. virtual ~Base() = default; — UB-prevention marks.

31. for (auto x : v) sum += x; on vector … A. is optimal B. copies each element; const auto& avoids it (habit for big element types) C. crashes D. modifies v

Answer: B. For doubles the copy is cheap; for objects it's the silent killer.

32. RAII means… A. random access iterator idiom B. resource lifetime bound to object lifetime — constructor acquires, destructor releases (exception-safe) C. RAM allocation D. a design pattern for GUIs

Answer: B. std::vector vs manual new[]/delete[] is Exhibit A.

33. A class holding double* data with a destructor that deletes MUST also handle… A. nothing else B. copy constructor + copy assignment (rule of three) — else double free on shallow copies C. printing D. templates

Answer: B. = delete them (or deep-copy) — the DynArray exam pattern.

34. Field of 10⁸ cells × 4 doubles ≈ … per copy, so pass-by-value is… A. 3.2 GB, absurd — pass const Field& B. 32 MB, fine C. 3.2 MB, fine D. irrelevant

Answer: A. 4·8·10⁸ B = 3.2 GB; ~3 GiB per copy at 400 GB/s ≈ 8 ms + doubled RAM — the quantified argument.

35. atoi("abc") returns 0 silently; robust argv parsing uses… A. atoi anyway B. strtol with end-pointer check (*end != '\\0' → reject) C. scanf D. exceptions

Answer: B. Validation + exit code = the bash-friendly main() contract.

36. std::vector<double> v; #pragma omp parallel for … v.push_back(f(i)); is broken because… A. vectors are serial-only B. push_back mutates shared size/capacity and may reallocate — a data race even though "each i differs"; pre-size and assign v[i] instead C. f is not thread-safe D. it's fine

Answer: B. The cross-chapter (Ch 14) trap: mutating a shared container ≠ writing distinct elements.


End of Chapter 11.