Course: INT1339 — C++ Programming Language
Textbook: Nguyen Duy Phuong, Nguyen Manh Son — C++ Programming Language Coursebook, 2020
Principles, Top-Down design strategy, advantages & disadvantages
Concept & classification, declaration & calling, parameter passing, scope & overloading
Defining structs, initialization & member access, struct pointers & arrays
Summary table + Refactoring MiniPOS V1.0.0 → V2.0.0 with functions & structs
Understand structured programming principles and the Top-Down design strategy for decomposing problems.
Declare, define, and call functions correctly, including void and value-returning functions.
Distinguish 3 mechanisms: pass by value, pass by reference (&), and pass by pointer (*).
Understand variable scope (local, global) and apply function overloading with different parameter lists.
Define and use struct data types, struct arrays, and struct pointers for grouping related data.
Refactor MiniPOS V1.0.0 → V2.0.0 by decomposing monolithic code into modular functions operating on structs.
int main() {
string names[100];
double prices[100];
int quantities[100];
int total = 0;
// main()
// Add product
// Display all
// Search? 15
// ALL mixed together!
}Hard to read, debug, and maintain
Must keep indices synchronized — error-prone
Same display logic repeated
Copy-paste needed for similar projects
Principles
Top-Down Design
Advantages & Disadvantages
Decomposition
Modularization
Separation of Data and Functions
Structured Programming is a methodology that organizes a program as a collection of functions (subroutines), where each function performs a specific, well-defined task.
Break a large problem into smaller sub-problems. "Store Management" → Add + Display + Search + Statistics
Each function = 1 module = 1 responsibility (Single Responsibility): addProduct(), displayAll()
Data is passed to functions via parameters, not hidden inside objects: void displayProduct(string name, double price)
Statements execute one after another, top-to-bottom.
if-else, switch-case for branching logic.
for, while, do-while for repetition.
Programs used GOTO statements to jump arbitrarily between lines, creating tangled, unreadable code paths.
10 PRINT "ENTER NUMBER"
20 INPUT N
30 IF N < 0 GOTO 70
40 PRINT "POSITIVE"
50 GOTO 80
60 GOTO 30
70 PRINT "NEGATIVE"
80 ENDEdsger Dijkstra published "Go To Statement Considered Harmful" in Communications of the ACM, arguing GOTO creates programs nearly impossible to reason about.
He proposed replacing GOTO with structured control flow: sequence, selection, and iteration — launching the structured programming movement dominant throughout the 1970s–1990s.
Decomposing Complex Problems into Manageable Sub-Problems
Top-Down Design (Stepwise Refinement): Start with the overall problem, decompose into smaller sub-problems, and repeat until each sub-task is simple enough to implement as a single function.
Top-Down design maps directly to microservices architecture used at Netflix and Amazon — each microservice handles one domain (User Service, Payment Service, Recommendation Service), just as each function handles one task.
Problem: Read scores for N students, calculate GPA, classify academic performance, and print a summary report.
When to Use
Limitations
Transition to Object-Oriented Programming
Which design strategy decomposes a large problem into smaller sub-problems, starting from the top level?
A. Bottom-Up Design
B. Top-Down Design
C. Left-Right Design
D. Random Decomposition
According to the Böhm–Jacopini Theorem, which 3 control structures are sufficient to implement any algorithm?
A. Sequence, Recursion, Exception
B. Sequence, Selection, Iteration
C. Input, Processing, Output
D. Declaration, Assignment, Return
What is the primary disadvantage of structured programming that OOP aims to solve?
A. Programs run too slowly
B. Cannot use loops
C. Data is not protected — any function can modify data freely D. Cannot print output
Top-Down Design starts with the overall problem and repeatedly breaks it down into smaller sub-problems until each is simple enough to implement directly as a function.
The Böhm–Jacopini Theorem (1966) proved any computable algorithm can be expressed using only Sequence (top-to-bottom), Selection (if-else), and Iteration (loops).
In structured programming, data and functions are separate — any function receiving data via parameters can modify it without restriction. OOP bundles data inside classes with access modifiers (private) to prevent unauthorized modification.
Concept & Classification
Declaration & Calling
Parameter Passing
Scope & Overloading
What Are Functions?
Library vs User-Defined
void vs Value-Returning
A function is a named block of code that performs a specific task. It can accept input data (parameters), process it, and optionally return a result.
300 lines in main() — hard to navigate. One chef does everything: takes orders, cooks, washes dishes. A bug could be anywhere.
Specialized stations: takeOrder(), grillMeat(), makeDessert(). main() reads like a table of contents — each function testable independently.
// void function — performs action
void greet(string name) {
cout << "Hello, " << name << "!";
}
// Value-returning function
double calcCircleArea(double r) {
return 3.14159 * r * r;
}Prototype
Definition
Call
Execution Flow
// STEP 1: FUNCTION PROTOTYPE (Declaration)
// Tells the compiler: "This function exists."
// Placed BEFORE main(), ends with semicolon
double calcArea(double radius);
// STEP 3: MAIN FUNCTION
int main() {
double r = 5.0;
// STEP 3: FUNCTION CALL
double area = calcArea(r); // Passes 5.0, receives result
cout << "Area = " << area; // Output: Area = 78.5398
return 0;
}
// STEP 2: FUNCTION DEFINITION (Implementation)
double calcArea(double radius) {
return 3.14159 * radius * radius;
}main() starts
│
├── double r = 5.0;
├── double area = calcArea(r);
│ (Execution PAUSES here)
│ ↓
│ calcArea(double radius) {
│ radius = 5.0 (copy of r)
│ return 3.14159 * 5.0 * 5.0;
│ → returns 78.5398
│ }
│ area = 78.5398 ← RETURN
│ (Execution RESUMES)
├── cout << "Area = " << area;
└── return 0;When a function is called, the calling function pauses execution.
Execution jumps to the called function's body and runs it completely.
When return is hit, control transfers back and the caller resumes from where it left off.
string encrypt(string text, int shift) {
string result = text;
for (int i = 0; i < result.length(); i++) {
if (isalpha(result[i])) {
char base = isupper(result[i]) ? 'A':'a';
result[i] = (result[i]-base+shift)%26+base;
}
}
return result;
}
// Decrypt by reversing — REUSES encrypt()!
string decrypt(string text, int shift) {
return encrypt(text, 26 - shift);
}decrypt() reuses encrypt() — shifting by 26 - shift reverses the encryption. No duplicate logic!
Only 6 meaningful lines in main() — easy to understand the entire program flow at a glance.
main() — Calling the Functions
int main() {
string msg = "Hello PTIT";
int key = 3;
string enc = encrypt(msg, key);
string dec = decrypt(enc, key);
cout << "Original: " << msg; // Hello PTIT
cout << "Encrypted: " << enc; // Khoor SWLW
cout << "Decrypted: " << dec; // Hello PTIT
}Pass by Value (Giá trị)
Pass by Reference (Tham chiếu)
Pass by Pointer (Con trỏ)
When to Use Each
void f(int x)
A copy of the value is passed. Cannot modify the original. High copy cost for large data.
void f(int &x)
An alias to the original variable. Can modify the original. No copy cost.
void f(int *x)
The memory address is passed. Modify original via *x. No copy cost. Supports nullptr.
Code Example
void tryToDouble(int x) {
x = x * 2; // Modifies LOCAL COPY only!
cout << "Inside: x = " << x; // 20
}
int main() {
int num = 10;
tryToDouble(num); // Passes a COPY of num
cout << "After: num = " << num; // Still 10!
}Output:
Inside function: x = 20
After call: num = 10 ← Original unchanged!Before call:
┌────────────────┐
│ main's num: 10 │
└────────────────┘
During tryToDouble():
┌────────────────┐
│ main's num: 10 │ (safe)
├────────────────┤
│ copy x: 20 │ (modified)
└────────────────┘
After return:
┌────────────────┐
│ main's num: 10 │
└────────────────┘
(copy destroyed)int, double, char, bool) where copying is cheapvoid actuallyDouble(int &x) { // &x = alias
x = x * 2; // Modifies ORIGINAL directly!
cout << "Inside: x = " << x; // 20
}
int main() {
int num = 10;
actuallyDouble(num);
cout << "After: num = " << num; // Now 20!
}
// Practical: Swapping Two Variables
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
// swap(x, y) — x and y swapped in main()!Before call:
┌────────────────┐
│ main's num: 10 │
└────────────────┘
During actuallyDouble():
┌────────────────┐
│ main's num: 20 │ ← &x
└────────────────┘
(x IS num — same memory cell)
After return:
┌────────────────┐
│ main's num: 20 │
└────────────────┘void doubleViaPointer(int *ptr) {
*ptr = (*ptr) * 2; // Dereference to modify
cout << "Inside: *ptr = " << *ptr; // 20
}
int main() {
int num = 10;
doubleViaPointer(&num); // Pass ADDRESS of num
cout << "After: num = " << num; // Now 20!
}Output:
Inside function: *ptr = 20
After call: num = 20 ← Original IS modified!Before call:
┌──────────────────┐
│ num: 10 │
│ Address: 0x1000 │
└──────────────────┘
During doubleViaPointer():
┌──────────────────┐
│ num: 20 │ ← *ptr writes here
│ Address: 0x1000 │
├──────────────────┤
│ ptr: 0x1000 │ (contains num's address)
└──────────────────┘new/delete) or arrays (decay to pointers)nullptr case// Pass by Value — broken swap
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
// Only local copies swapped!
}
// Pass by Reference — correct swap
void swapByRef(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
// Pass by Pointer — correct swap
void swapByPtr(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}int main() {
int x = 5, y = 10;
swapByValue(x, y); // x=5, y=10 (unchanged)
swapByRef(x, y); // x=10, y=5 (swapped!)
swapByPtr(&x, &y); // x=5, y=10 (swapped back!)
}Local vs Global Variables
Name Shadowing
Overloaded Functions
int globalCounter = 0; // GLOBAL
// accessible everywhere
void increment() {
int localStep = 1; // LOCAL
// only inside increment()
globalCounter += localStep; // OK ✓
}
int main() {
int localVar = 100; // LOCAL
// only inside main()
increment();
cout << globalCounter; // OK ✓ (global)
cout << localVar; // OK ✓ (local here)
// cout << localStep; // ✗ ERROR!
// localStep not in scope
}int x = 100; // Global x
void demo() {
int x = 5; // Local x SHADOWS global
cout << x; // Prints 5 (local)
cout << ::x; // Prints 100 (global, :: operator)
}Pass data via parameters rather than relying on global state.
const int MAX_PRODUCTS = 100; — safe, readable, no mutation risk.
They create hidden dependencies between functions — a major source of bugs.
// Overload 1: Base price, no discount
double calcPrice(double price) {
return price;
}
// Overload 2: Price with percentage discount
double calcPrice(double price, double discountPct) {
return price * (1 - discountPct / 100);
}
// Overload 3: With maximum discount cap
double calcPrice(double price, double discountPct,
double maxDiscount) {
double disc = price * discountPct / 100;
return price - min(disc, maxDiscount);
}The compiler selects the correct overload based on the number and types of arguments at the call site — resolved at compile time, not runtime.
int main() {
cout << calcPrice(100000); // 100000 → Overload 1
cout << calcPrice(100000, 10); // 90000 → Overload 2
cout << calcPrice(100000, 50, 20000); // 80000 → Overload 3
}Given void update(int x) { x = x + 5; } and int a = 10; update(a); → What is the value of a after the call?
A. 10
B. 15
C. 5
D. Compile Error
Which parameter passing mechanism creates an alias to the original variable?
A. Pass by Value
B. Pass by Pointer
C. Pass by Reference
D. Pass by Copy
Which of the following is NOT a valid function overload pair?
A. void f(int x) and void f(double x)
B. void f(int x) and void f(int x, int y)
C. int f(int x) and double f(int x) (different return type only)
D. void f(int x) and void f(string x)
Pass by value — x is a copy of a. Modifying x inside the function does not affect the original a in main(). To modify a, use void update(int &x).
Pass by reference (&) makes the parameter an alias (another name) for the original variable. Pass by pointer passes the address; pass by value passes a copy.
Function overloading requires different parameter lists. Changing only the return type is NOT sufficient — the compiler cannot determine which overload to call based on return type alone, since the return value might be ignored.
Defining Structs
Initialization & Access
Struct Pointers & Arrays
Why Structs?
Syntax
Nested Structs
typedef
string names[100]; // Product name
double prices[100]; // Unit price
int quantities[100]; // Stock quantity
// To access Product #3:
cout << names[3] << " costs "
<< prices[3] << " VND";
// What if we use prices[4] with names[3]?
// → WRONG DATA!names[i], prices[i] must always match// WITHOUT struct:
string names[100];
double prices[100];
int quantities[100];
string categories[100];
// 4 parallel arrays!
// WITH struct:
struct Product {
string name;
double price;
int quantity;
string category;
};
Product products[100];
// All data in ONE place!A struct (structure) is a user-defined data type that groups multiple variables of different types under a single name.
struct StructName {
DataType member1;
DataType member2;
DataType member3;
}; // ← Don't forget the semicolon!struct Product {
string name; // Product name
double price; // Unit price
int quantity; // Stock quantity
string category; // Product category
};struct Student {
string id; // "B21DCMM001"
string fullName; // Full name
double gpa; // Grade Point Average
int credits; // Total credits completed
};int or double), not a variablestring, int, double, even another struct; after the closing brace } is mandatory!struct Date {
int day;
int month;
int year;
};
struct Student {
string id;
string fullName;
Date birthDate; // Nested struct!
double gpa;
};
// Usage:
Student s;
s.fullName = "Nguyen Van A";
s.birthDate.day = 15;
s.birthDate.month = 3;
s.birthDate.year = 2004;A shipping order contains sender info, receiver info, and package details — each is a nested struct.
struct Address {
string street;
string city;
string zipCode;
};
struct Order {
int orderId;
Address sender; // Nested!
Address receiver; // Nested!
double totalWeight;
};// C-style: must write "struct Product" every time
struct Product {
string name;
double price;
};
struct Product p1; // C-style declaration
// Using typedef: create alias "Product"
typedef struct {
string name;
double price;
} Product;
Product p1; // Cleaner declarationstruct Product {
string name;
double price;
};
// Works directly in C++ — no typedef needed!
Product p1;
Product p2 = {"Laptop", 15000000};Creating Struct Variables
Dot Operator
Passing Structs to Functions
struct Product { string name; double price; int quantity; string category; };
// Method 1: Brace initialization (must match member order)
Product p1 = {"Laptop", 15000000, 5, "Electronics"};
// Method 2: Default construction + individual assignment
Product p2;
p2.name = "Mouse"; p2.price = 250000;
p2.quantity = 20; p2.category = "Accessories";
// Method 3: C++11 brace initialization
Product p3 = {"Keyboard", 500000, 10, "Accessories"};
// Method 4: Copy from another struct
Product p4 = p1; // All members copied from p1cout << p1.name; // "Laptop"
cout << p1.price; // 15000000
p1.quantity -= 1; // Decrement stock
p1.price *= 0.9; // Apply 10% discount// Pass by Value (Creates a Copy — EXPENSIVE!)
void displayProduct(Product p) {
cout << p.name << " | " << p.price;
}
// Pass by Reference (Recommended for Modification)
void applyDiscount(Product &p, double pct) {
p.price *= (1 - pct / 100); // Modifies original!
}
// Pass by const Reference (Recommended for Read-Only)
void displayProduct(const Product &p) {
cout << p.name << " | " << p.price;
// p.price = 0; //Arrow Operator (->)
Struct Arrays
Sorting Struct Arrays
Product laptop = {"Laptop", 15000000, 5, "Electronics"};
Product* ptr = &laptop;
// Two equivalent ways to access via pointer:
cout << ptr->name; // Arrow operator (preferred)
cout << (*ptr).name; // Dereference + dot (verbose)
// Modify through pointer:
ptr->price *= 0.85; // Apply 15% discount
ptr->quantity -= 1; // Decrease stock┌────────────────────────────┐
│ laptop (Address: 0x1000) │
│ .name = "Laptop" │
│ .price = 15000000 │
│ .quantity = 5 │
└────────────────────────────┘
▲ points to
┌────────────────────────────┐
│ ptr = 0x1000 (Pointer Addr: 0x2000)│
└────────────────────────────┘
ptr->name ≡ (*ptr).name ≡ laptop.nameStruct Array Example
const int MAX = 100;
Product inventory[MAX]; // Array of 100 Products
int total = 0;
// Add products:
inventory[total] = {"Keyboard", 500000, 10, "Accessories"};
total++;
inventory[total] = {"Monitor", 3500000, 3, "Electronics"};
total++;
// Display all products:
for (int i = 0; i < total; i++) {
cout << i+1 << ". "
<< inventory[i].name << " | "
<< inventory[i].price << " VND | Qty: "
<< inventory[i].quantity << endl;
}inventory[0]
┌─────────────────────┐
│ name: "Keyboard" │ Address: 0x1000
│ price: 500000 │
│ quantity: 10 │
│ category: "Access." │
└─────────────────────┘
inventory[1]
┌─────────────────────┐
│ name: "Monitor" │ Address: 0x1050
│ price: 3500000 │
│ quantity: 3 │
│ category: "Electr." │
└─────────────────────┘void sortByPrice(Product inv[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (inv[j].price > inv[j+1].price) {
swap(inv[j], inv[j+1]); // Swap entire structs
}
}
}
}
// Sort by name (alphabetical)
void sortByName(Product inv[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (inv[j].name > inv[j+1].name) {
swap(inv[j], inv[j+1]);
}
}
}
}swap(inv[j], inv[j+1]) swaps the entire struct (all members together) — no risk of desynchronized parallel arrays!
Compare with V1.0.0 where you'd need to swap names[j], prices[j], AND quantities[j] separately.
Structs serve as the foundation for implementing Abstract Data Types (ADTs) — data structures where implementation details are separated from the interface.
struct Stack {
int data[100];
int top; // Index of top element (-1 = empty)
};
void push(Stack &s, int value) { s.data[++s.top] = value; }
int pop(Stack &s) { return s.data[s.top--]; }Given Product p = {"Laptop", 15000000, 5, "Electronics"}; and Product *ptr = &p; → Which syntax correctly accesses the product name via the pointer?
A. ptr.name
B. ptr->name
C. *ptr->name
D. ptr::name
What is the primary advantage of using a struct array over parallel arrays?
A. Structs use less memory
B. Structs run faster
C. All related data is grouped together, eliminating index synchronization issues
D. Structs are required by the C++ standard
When passing a large struct to a function for read-only display, what is the recommended parameter type?
A. Product p (by value)
B. Product &p (by reference) C. const Product &p (const reference)
D. Product *p (by pointer)
The arrow operator -> is used to access struct members through a pointer. ptr.name is incorrect because ptr is a pointer, not a struct variable. (*ptr).name also works but ptr->name is the standard shorthand.
With parallel arrays, you must ensure names[i], prices[i], and quantities[i] always correspond to the same product. With a struct array, products[i] contains ALL data for one product — impossible to desynchronize.
const Product &p passes by reference (no copying cost) while preventing accidental modification (const). Pass by value creates an unnecessary copy; pass by non-const reference allows unintended modifications.
Knowledge Summary
MiniPOS V1.0.0 → V2.0.0 Refactoring
Homework
Systematic Review of All Content
(Part 1 of 2)
(Part 2 of 2)
Refactoring MiniPOS V1.0.0 → V2.0.0 with Functions & Structs
string names[100];
double prices[100];
int quantities[100];struct Product {
string name;
double price;
int quantity;
};
Product inventory[MAX];
// 4 Function Prototypes:
void printProduct(const Product &p, int index);
void addProduct(Product inv[], int &total);
void displayAll(const Product inv[], int total);
void searchProduct(const Product inv[], int total,
string keyword);#include <iostream>
#include <limits>
#include <string>
using namespace std;
// Struct dinh nghia kieu du lieu San pham
struct Product {
string name;
double price;
int quantity;
};
const int MAX = 100;
// Khai bao nguyen mau ham (Function Prototypes)
void printProduct(const Product &p, int index);
void addProduct(Product inv[], int &total);
void displayAll(const Product inv[], int total);
void searchProduct(const Product inv[],
int total, string keyword);// 1. Ham them san pham (truyen tham chieu int &total de cap nhat so luong tong)
void addProduct(Product inv[], int &total) {
if (total < MAX) {
cin.ignore(numeric_limits::max(), '\n');
cout << "Ten SP: ";
getline(cin, inv[total].name);
do {
cout << "Gia (>0): ";
cin >> inv[total].price;
} while (inv[total].price <= 0);
do {
cout << "SL (>0): ";
cin >> inv[total].quantity;
} while (inv[total].quantity <= 0);
total++;
cout << "=> Them thanh cong!\n";
} else {
cout << "MANG DA DAY!\n";
}
}Must be a reference — total must increase by 1 in the caller's scope (main()).
Prevents writing beyond array bounds — essential safety guard.
Re-prompts until user enters a valid positive value for price and quantity.
Flushes entire input buffer before getline() to avoid the skipped-line bug.
// Ham ho tro in thong tin 1 san pham (Dung chung cho Hien thi & Tim kiem)
void printProduct(const Product &p, int index) {
cout << index << ". " << p.name << " | Gia: " << p.price
<< " | SL: " << p.quantity << endl;
}
// 2. Ham hien thi danh sach san pham (Read-only)
void displayAll(const Product inv[], int total) {
cout << "\n--- DANH SACH SAN PHAM ---\n";
if (total == 0) {
cout << "Chua co san pham nao.\n";
} else {
for (int i = 0; i < total; i++) {
printProduct(inv[i], i + 1);
}
}
}
// 3. Ham tim kiem san pham theo ten (Read-only)
void searchProduct(const Product inv[], int total, string keyword) {
bool found = false;
cout << "\n--- KET QUA TIM KIEM ---\n";
for (int i = 0; i < total; i++) {
if (inv[i].name.find(keyword) != string::npos) {
printProduct(inv[i], i + 1);
found = true;
}
}
if (!found) {
cout << "Khong tim thay san pham.\n";
}
}Both displayAll() and searchProduct() call printProduct() — output format is defined once, no duplication.
Both displayAll and searchProduct are read-only — const prevents accidental modification of any product.
displayAll shows "Chua co san pham nao." when empty; searchProduct shows "Khong tim thay san pham." when no match.
find() returns string::npos when keyword is not found; otherwise returns the start index — enabling partial name matching.
// Three separate arrays — must update ALL three
string names[100];
double prices[100];
int quantities[100];
int total = 0;
void addProduct(string name, double price, int qty) {
names[total] = name; // array 1
prices[total] = price; // array 2
quantities[total] = qty; // array 3
total++;
// Miss one → data is out of sync!
}// One struct bundles all fields together
struct Product {
string name;
double price;
int quantity;
};
Product inventory[100];
int total = 0;
void addProduct(Product inv[], int &total) {
// inv[total] is ONE struct — all fields together
getline(cin, inv[total].name);
cin >> inv[total].price;
cin >> inv[total].quantity;
total++;
// One record — impossible to desync!
}Swap or assign one struct instead of remembering to update three separate arrays in sync.
All fields belong to the same object — no index mismatch where names[2] accidentally pairs with prices[3].
inventory[i].name is self-documenting and clearer than the fragmented names[i] across multiple arrays.
int main() {
Product inventory[MAX];
int total = 0;
int choice;
do {
cout << "\n===== MINIPOS V2.0.0 =====\n";
cout << "1. Them san pham\n";
cout << "2. Hien thi danh sach\n";
cout << "3. Tim kiem\n";
cout << "0. Thoat\n";
cout << "Chon: ";
cin >> choice;
switch (choice) {
case 1:
addProduct(inventory, total);
break;
case 2:
displayAll(inventory, total);
break;
case 3: {
cin.ignore(numeric_limits<streamsize>::max(), '\n');
string keyword;
cout << "Nhap ten san pham can tim: ";
getline(cin, keyword);
searchProduct(inventory, total, keyword);
break;
}
case 0:
cout << "Dang thoat chuong trinh...\n";
break;
default:
cout << "Lua chon khong hop le!\n";
}
} while (choice != 0);
return 0;
}main() displays the menu and delegates each task to a specific function — each case is a single function call.
Compare with V1.0.0 where each case had 10–20 lines of inline code. V2.0.0 main() calls only 3 functions directly — addProduct, displayAll, and searchProduct. printProduct is called internally by displayAll and searchProduct, not by main().
To add a new feature: write a new function and add a new case. No need to modify existing logic.
editProduct() and deleteProduct() functions to the existing codebase.(Part 1 of 2)
In MiniPOS V2.0.0, why is total passed by reference (int &total) in addProduct() but by value (int total) in displayAll()?
A. By reference is always faster
B. addProduct must increment total in the caller; displayAll only reads it
C. displayAll cannot use references
D. No difference
In addProduct(), why is cin.ignore(numeric_limits<streamsize>::max(), '\n') called before getline()?
A. To slow down the program
B. To flush the newline left in the buffer by the previous cin >> choice
C. getline() requires the buffer to be filled
D. It is optional
Given struct Student { string name; double gpa; }; and void display(Student s), what happens when display(s1) is called?
A. s1 passed directly — modifications affect s1
B. A copy of s1 is created — modifications do NOT affect s1
C. Only s1.name is passed
D. Compile error
(Part 2 of 2)
In searchProduct(), the parameter is const Product inv[]. What does const prevent?
A. Iterating over the array
B. Accidentally modifying any product's data
C. The keyword from being found
D. The array from being passed
In MiniPOS V2.0.0, why does addProduct() use a do-while loop for price input instead of a regular if check?
A. do-while runs faster
B. To keep re-prompting until a valid positive price is entered
C. Because if cannot check <= 0
D. To display the prompt exactly twice
(Part 1 of 2)
addProduct adds a new product and must increment the counter visible in main() — requires pass by reference. displayAll only reads the count to loop — no modification needed.
cin >> choice reads only the integer and leaves '\n' in the buffer. If getline() runs immediately after, it reads that leftover newline and appears to skip input. cin.ignore(max, '\n') discards everything up to and including the newline.
Structs follow the same pass-by-value rules as primitive types. Without &, the function receives a complete copy. To modify the original, use Student &s.
(Part 2 of 2)
const Product inv[] tells the compiler that searchProduct is read-only. If any line tried to assign inv[i].name = ..., the compiler would immediately report an error — catching the bug at compile time.
A do-while loop guarantees the body executes at least once, then checks the condition. If the user enters 0 or a negative value, the loop continues prompting. A plain if check would only reject invalid input once.
(Part 1 of 2)
Break large programs into single-responsibility functions, each handling one task.
Always declare a prototype before main(), define the implementation, and call with matching arguments.
Value → safe copy; Reference (&) → modify original; Pointer (*) → modify via address.
(Part 2 of 2)
Replace parallel arrays with structs to keep related fields together — safer, cleaner, more maintainable.
Local variables live inside their function; prefer local over global to avoid hidden dependencies.
Same function name with different parameter lists: calcPrice(p), calcPrice(p, discount), calcPrice(p, discount, max).
struct Product { string name; double price; };
void display(const Product &p) {
cout << p.name << ": " << p.price;
}
display(laptop);class Product {
private:
string name; double price;
public:
void display() {
cout << name << ": " << price;
}
};
laptop.display(); // Object "knows" how to display!Nguyen Duy Phuong, Nguyen Manh Son — C++ Programming Language Coursebook, 2020
Robert C. Martin — Clean Code, Chapter 3: "Functions" (Prentice Hall, 2008)
"Functions should do one thing. They should do it well. They should do it only." — Robert C. Martin
Chapter 2 — Structured Programming with C++