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.cppandexercises_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 avsbox::avs global::a). - Pointers (
int *p), addresses (&x), dereferencing (*p). - Arrays and pointer arithmetic.
- Conditionals (
if/else,?:),for/whileloops. - 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
swapusing 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
Complexexercise — 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:
Compile and run:
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 ofx.*p— value pointed to byp(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::methoddefinition 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
avs callee'sa. - Class diagram —
Complexwith 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\):
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:
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\)):
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
বাংলায়: 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)¶
(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 accesses the global.
Example 6.4 — Namespace (example04.cpp)¶
Example 6.5 — Pointers (example05.cpp)¶
Example 6.6 — Array iteration (example06.cpp)¶
Example 6.7 — Pointer arithmetic over array (example07.cpp)¶
(Each address is +4 bytes apart since int is 4 bytes.)
Example 6.8 — Conditional filter (example08.cpp)¶
(Only even values printed.)
Example 6.9 — Function: is_prime (example09.cpp)¶
Example 6.10 — Call by value vs reference (example10.cpp)¶
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:
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:
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,*bto 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/imggetters. - 2
addcorrectness. - 2
multiplycorrectness. - 1 modulus formula.
- 1 main: construct + invoke.
- 1 expected output.
Common mistakes.
- Forgot
;after class definition. - Constructor with
voidreturn type → compile error (constructors have no return type). - Used
pow(a,2)+pow(b,2)instead ofa*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:
বাংলায়: 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:
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.
- Headers
<iostream>,<cmath>forstd::sqrt. class Complex { private: float a,b; public: … };- Constructor with member-init list:
Complex(float x,float y) : a(x), b(y) {}. - Member functions defined outside with
Complex::name. - add: \((c_1.a+c_2.a) + (c_1.b+c_2.b)i\).
- multiply: \((ac-bd) + (ad+bc)i\).
- 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¶
example10.cpp predicted output:
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
- Compile hello.cpp. →
g++ hello.cpp -o hello. - Print "hi". →
std::cout<<"hi"<<std::endl; - Declare an int. →
int a=5; - Address of a. →
&a. - Pointer to a. →
int *p=&a;
Intermediate
- Loop over array length 5. →
for(int i=0;i<5;++i) ... - Pass int by reference. →
void f(int& a). - Class with public method display(). → see Complex.
- Constructor with init list. →
T():a(0){}. - Why is
using namespace stdbad in headers? → name pollution.
Hard
- Why does
f(int)not modify the caller's variable? → call-by-value copies. - What's wrong with
int *p; *p=5;? → uninitialised pointer (UB/segfault). - Difference between
deleteanddelete[]. → single object vs array. - Why is endl slow? → flushes the buffer.
- Write a function returning an object of class C. → return by value uses copy/move.
Very Hard
- Why do CFD solvers prefer
const Field&overField? → avoid expensive copies. - Difference between stack and heap allocation. → automatic vs
new/delete, lifetime. - What happens if you forget
;after class? → cascading errors at the next declaration.
Deep Integration
- How does pointer arithmetic underpin SIMD vectorisation? → contiguous memory, stride 1.
- Compare RAII to manual new/delete. → constructor acquires, destructor releases — exception-safe.
Coding/Command
- Implement
swap(int& a, int& b). → see harder version in §8. - Add
operator+to Complex. → see 9.7.
Debugging
*p=10;placed beforeint a; p=&a;— what happens? → p is dereferenced uninitialised; segfault/UB.class C{int x;}; C c; c.x=5;— error? → x is private by default; needpublic:or an accessor.
Long Written
- (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:
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 |
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:
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:
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\).
বাংলা ইঙ্গিত: মাইনাস-চিহ্নগুলো সাবধানে: \(-bd = -(-3)(4) = +12\) — sign-এর ভুলেই বেশিরভাগ নম্বর যায়।Q2. (8 marks) This compiles but crashes. Find the bug and fix it:
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}
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
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; }
};
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];
& না দিলে প্রতি 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;
}
./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 vectorconst 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.