Object-Oriented ProgrammingObject-Oriented Programming
Classes, objects, inheritance, polymorphism — the C++ ideas BUET loves to test. Class, object, inheritance, polymorphism — C++ এর যেসব idea BUET পরীক্ষায় বারবার আসে।
- Classes and ObjectsClasses and Objects
- Encapsulation and AbstractionEncapsulation and Abstraction
- Constructors and DestructorsConstructors and Destructors
- InheritanceInheritance
- PolymorphismPolymorphism
- Other C++ ConceptsOther C++ Concepts
- Java Corner (Real Exam Questions!)Java Corner (Real Exam Questions!)
- Practice QuestionsPractice Questions
1. Classes and Objects1. Classes and Objects
A class is a blueprint. It describes what data a thing has, and what actions it can do. An object is a real thing made from that blueprint.
Think of it like this: "Student" is a class. It says every student has a name and a roll. "Rahim, roll 101" is an object. You can make many objects from one class.
- Class = idea / design / template. Takes no memory by itself.
- Object = real instance. Each object gets its own memory for the data members.
- The data inside a class are called data members. The functions inside are called member functions (or methods).
Class হলো একটা blueprint বা নকশা। এটা বলে দেয় কোনো জিনিসের কী কী data থাকবে আর সে কী কী কাজ করতে পারবে। Object হলো সেই নকশা থেকে বানানো আসল জিনিস।
এভাবে ভাবুন: "Student" একটা class। এটা বলে প্রতিটা student-এর একটা name আর roll থাকবে। "Rahim, roll 101" হলো একটা object। একটা class থেকে অনেকগুলো object বানানো যায়।
- Class = idea / design / template। এটা নিজে কোনো memory নেয় না।
- Object = আসল instance। প্রতিটা object-এর data member-দের জন্য আলাদা memory থাকে।
- Class-এর ভেতরের data-কে বলে data member। ভেতরের function-কে বলে member function (বা method)।
#include <iostream>
using namespace std;
class Student {
public:
string name; // data member
int roll; // data member
void show() { // member function
cout << name << " " << roll << endl;
}
};
int main() {
Student a, b; // two objects
a.name = "Rahim"; a.roll = 101;
b.name = "Karim"; b.roll = 102;
a.show();
b.show();
return 0;
}
// Output:
// Rahim 101
// Karim 102
Here a and b each have their own name and roll. Changing a.roll does not touch b.roll.
এখানে a আর b — দুজনের নিজের নিজের name আর roll আছে। a.roll বদলালে b.roll বদলায় না।
Access specifiers: public, private, protectedAccess specifiers: public, private, protected
Access specifiers control who can touch a member from outside the class.
Access specifier ঠিক করে দেয় class-এর বাইরে থেকে কে কোন member ধরতে পারবে।
| SpecifierSpecifier | Inside the classClass-এর ভেতরে | In derived classDerived class-এ | Outside (main etc.)বাইরে (main ইত্যাদি) |
|---|---|---|---|
public |
Yesহ্যাঁ | Yesহ্যাঁ | Yesহ্যাঁ |
protected |
Yesহ্যাঁ | Yesহ্যাঁ | Noনা |
private |
Yesহ্যাঁ | Noনা | Noনা |
class Account {
private:
double balance; // hidden
public:
void setBalance(double b) { balance = b; }
double getBalance() { return balance; }
};
int main() {
Account acc;
acc.balance = 5000; // ERROR! balance is private
acc.setBalance(5000); // OK
cout << acc.getBalance(); // OK, prints 5000
}
struct vs class in C++C++ এ struct vs class
In C++, a struct can also have member functions, constructors, even inheritance. The only real difference is the default access.
C++ এ struct-এও member function, constructor, এমনকি inheritance থাকতে পারে। আসল পার্থক্য একটাই — default access।
struct |
class |
|
|---|---|---|
| Default member accessDefault member access | public |
private |
| Default inheritance modeDefault inheritance mode | public |
private |
| Common useসাধারণ ব্যবহার | Simple data bundlesSimple data একসাথে রাখা | Full OOP with hidden dataData লুকিয়ে full OOP |
The this pointerthis pointer
Inside a member function, this is a pointer to the object that called the function. It is passed automatically. Two common uses:
- Separate a data member from a parameter with the same name:
this->x = x; - Return the current object for chaining:
return *this;
Member function-এর ভেতরে this হলো একটা pointer, যেটা সেই object-কে point করে যে function-টা call করেছে। এটা automatic pass হয়। দুইটা common ব্যবহার:
- Data member আর same নামের parameter আলাদা করা:
this->x = x; - Chaining-এর জন্য current object return করা:
return *this;
class Box {
int w;
public:
Box& setW(int w) {
this->w = w; // left w = member, right w = parameter
return *this; // return the object itself
}
void show() { cout << "w = " << w << endl; }
};
int main() {
Box b;
b.setW(10).show(); // chaining works because setW returns *this
}
// Output:
// w = 10
class is private, of struct is public. (2) A class takes no memory; sizeof an empty class object is 1 byte (so each object has a unique address). (3) this is not available inside static member functions — they belong to the class, not to any object.
পরীক্ষায় প্রিয় প্রশ্ন: (১) class-এর default access private, struct-এর public। (২) Class নিজে memory নেয় না; empty class-এর object-এর sizeof হয় 1 byte (যাতে প্রতিটা object-এর আলাদা address থাকে)। (৩) static member function-এর ভেতরে this পাওয়া যায় না — কারণ ওগুলো object-এর না, class-এর।
Class design: many-to-many (Employee — Project)Class design: many-to-many (Employee — Project)
BUET sometimes gives a real-life situation and asks you to design the classes. A classic one: one Employee can work on many Projects, and one Project can have many Employees. This is a many-to-many relationship. The clean trick: put a third class in the middle — an association class, here called Assignment. One Assignment object means "this employee works on this project". It is also the natural home for extra data that belongs to the pair, like hours per week or role.
BUET মাঝে মাঝে একটা real-life situation দিয়ে বলে class design করতে। Classic উদাহরণ: একজন Employee অনেকগুলো Project-এ কাজ করতে পারে, আবার একটা Project-এ অনেকজন Employee থাকতে পারে। এটাই many-to-many relationship। সহজ সমাধান: মাঝখানে তৃতীয় একটা class বসান — একটা association class, এখানে নাম Assignment। একটা Assignment object মানে "এই employee এই project-এ কাজ করে"। যে data জোড়াটার নিজের (যেমন hours per week বা role), তার জায়গাও এই class-এই।
| RelationshipRelationship | Meaningমানে |
|---|---|
Employee 1 — * Assignment |
One employee has many assignmentsএকজন employee-র অনেকগুলো assignment থাকে |
Project 1 — * Assignment |
One project has many assignmentsএকটা project-এর অনেকগুলো assignment থাকে |
Employee * — * Project |
The two 1-to-many links together give the many-to-manyদুইটা 1-to-many link মিলে many-to-many তৈরি হয় |
class Employee; // forward declarations
class Project;
class Assignment { // association class: one (employee, project) pair
public:
Employee* employee; // who
Project* project; // on what
int hoursPerWeek; // extra data about the PAIR lives here
string role; // e.g., "tester", "lead"
};
class Employee {
public:
int id;
string name;
vector<Assignment*> assignments; // one employee → many assignments
};
class Project {
public:
int code;
string title;
vector<Assignment*> assignments; // one project → many assignments
};
To list all projects of an employee: walk her assignments and follow each project pointer. To list all employees of a project: same idea from the other side.
একজন employee-র সব project বের করতে: তার assignments ঘুরে প্রতিটার project pointer follow করুন। একটা project-এর সব employee বের করতে: উল্টো দিক থেকে একই idea।
vector) of the middle class. It is the same idea as a junction table in databases.
ঠিক এই design-টাই (Employees আর Projects, many-to-many) BUET October 2018 পরীক্ষায় এসেছিল। Pattern-টা মনে রাখুন: many-to-many = দুইটা class + মাঝখানে একটা association class, আর দুই পাশের class-ই মাঝের class-টার একটা collection (যেমন vector) রাখে। Database-এর junction table-এর idea-টাই এখানে।
2. Encapsulation and Abstraction2. Encapsulation and Abstraction
EncapsulationEncapsulation
Encapsulation means wrapping data and the functions that work on that data into one unit (a class), and hiding the data from outside. Outsiders talk to the object only through public functions.
Why hide data?
- Safety: no one can set an invalid value directly (like a negative balance).
- Control: you can add checks inside setters.
- Freedom to change: you can change the inside of the class later without breaking outside code.
Encapsulation মানে data আর সেই data নিয়ে কাজ করা function-গুলোকে এক unit-এ (class-এ) বেঁধে ফেলা, আর data-কে বাইরের থেকে লুকিয়ে রাখা। বাইরের কেউ object-এর সাথে কথা বলবে শুধু public function দিয়ে।
Data লুকাই কেন?
- Safety: কেউ সরাসরি ভুল value বসাতে পারবে না (যেমন negative balance)।
- Control: setter-এর ভেতরে check বসানো যায়।
- বদলানোর স্বাধীনতা: পরে class-এর ভেতরটা বদলালেও বাইরের code ভাঙে না।
class BankAccount {
private:
double balance; // hidden data
public:
BankAccount() { balance = 0; }
void deposit(double amount) { // setter-style
if (amount > 0) balance += amount; // validation!
}
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
double getBalance() { return balance; } // getter
};
int main() {
BankAccount acc;
acc.deposit(1000);
acc.deposit(-500); // ignored: validation blocks it
acc.withdraw(300);
cout << acc.getBalance(); // 700
}
// Output: 700
Nobody can write acc.balance = -999;. The data is safe behind the public functions.
কেউ acc.balance = -999; লিখতে পারবে না। Public function-এর পেছনে data নিরাপদ।
AbstractionAbstraction
Abstraction means showing only the essential things and hiding the details of how it works. When you press a car's brake, you do not think about the brake wires. Same idea: the user of a class sees what it does, not how.
In C++, abstraction is done by:
- Public interface + private implementation (access specifiers).
- Abstract classes — classes with at least one pure virtual function. They give only the "what", and derived classes fill in the "how". You cannot create an object of an abstract class.
Abstraction মানে শুধু দরকারি জিনিসটা দেখানো, আর কীভাবে কাজটা হয় সেই details লুকানো। গাড়ির brake চাপলে আপনি brake-এর ভেতরের তার নিয়ে ভাবেন না। একই idea: class-এর user দেখে কী হয়, কীভাবে হয় তা না।
C++ এ abstraction করা হয়:
- Public interface + private implementation দিয়ে (access specifier)।
- Abstract class দিয়ে — যে class-এ অন্তত একটা pure virtual function আছে। এরা শুধু "কী করতে হবে" বলে, derived class "কীভাবে" সেটা লেখে। Abstract class-এর object বানানো যায় না।
class Shape { // abstract class
public:
virtual double area() = 0; // pure virtual: only "what"
};
class Circle : public Shape {
double r;
public:
Circle(double r) { this->r = r; }
double area() { return 3.1416 * r * r; } // the "how"
};
int main() {
// Shape s; // ERROR: cannot create object of abstract class
Circle c(2);
cout << c.area(); // 12.5664
}
| Encapsulation | Abstraction | |
|---|---|---|
| Main ideaমূল idea | Hide the data, bundle data + functionsData লুকানো, data + function একসাথে বাঁধা | Hide the implementation details, show only interfaceImplementation details লুকানো, শুধু interface দেখানো |
| FocusFocus | "How data is protected""Data কীভাবে protect হয়" | "What the object can do""Object কী করতে পারে" |
| C++ toolsC++ tools | private, protected, getters/setters |
abstract class, pure virtual function, interfaceabstract class, pure virtual function, interface |
| LevelLevel | Implementation levelImplementation level | Design levelDesign level |
3. Constructors and Destructors3. Constructors and Destructors
A constructor is a special member function that runs automatically when an object is created. Rules:
- Same name as the class.
- No return type (not even
void). - Can be overloaded (many constructors with different parameters).
A destructor (~ClassName()) runs automatically when an object is destroyed. It takes no parameters, has no return type, and there can be only one — so it cannot be overloaded.
Constructor হলো special member function, যেটা object তৈরি হওয়ার সময় automatic চলে। নিয়ম:
- Class-এর নামের সাথে same নাম।
- কোনো return type নেই (
void-ও না)। - Overload করা যায় (আলাদা parameter দিয়ে অনেকগুলো constructor)।
Destructor (~ClassName()) object destroy হওয়ার সময় automatic চলে। এটা কোনো parameter নেয় না, return type নেই, আর একটাই থাকতে পারে — তাই overload করা যায় না।
Types of constructorsConstructor-এর ধরন
#include <iostream>
using namespace std;
class Point {
int x, y;
public:
Point() { // 1. default constructor
x = 0; y = 0;
cout << "Default\n";
}
Point(int a, int b) { // 2. parameterized constructor
x = a; y = b;
cout << "Parameterized\n";
}
Point(const Point &p) { // 3. copy constructor
x = p.x; y = p.y;
cout << "Copy\n";
}
void show() { cout << "(" << x << "," << y << ")\n"; }
};
int main() {
Point a; // Default
Point b(3, 4); // Parameterized
Point c = b; // Copy (same as Point c(b);)
c.show(); // (3,4)
}
// Output:
// Default
// Parameterized
// Copy
// (3,4)
Point c = b;, (2) an object is passed to a function by value, (3) an object is returned by value from a function. The copy constructor parameter must be a reference (const Point &p) — if it took the object by value, that pass-by-value would itself need a copy, causing infinite recursion.
Copy constructor কখন call হয়? এই ৩টা case মুখস্থ রাখুন: (১) এক object থেকে আরেক object তৈরি: Point c = b;, (২) object কোনো function-এ by value pass করলে, (৩) function থেকে object by value return করলে। Copy constructor-এর parameter অবশ্যই reference হতে হবে (const Point &p) — value নিলে সেই pass-by-value-র জন্যই আবার copy লাগত, ফলে infinite recursion হতো।
Constructor and destructor orderConstructor আর destructor-এর order
Objects are destroyed in the reverse order of creation (like a stack). Also, in inheritance: base constructor runs first, derived constructor next; destructors run in reverse (derived first, then base).
Object যেভাবে তৈরি হয়, destroy হয় তার উল্টা order-এ (stack-এর মতো)। আর inheritance-এ: আগে base constructor, তারপর derived constructor; destructor চলে উল্টা করে (আগে derived, পরে base)।
class A {
int id;
public:
A(int i) { id = i; cout << "C" << id << " "; }
~A() { cout << "D" << id << " "; }
};
int main() {
A a1(1);
A a2(2);
A a3(3);
return 0;
}
// Output:
// C1 C2 C3 D3 D2 D1
Creation order: 1, 2, 3. Destruction order: 3, 2, 1 — last created, first destroyed. তৈরির order: 1, 2, 3। Destroy-এর order: 3, 2, 1 — যে শেষে তৈরি, সে আগে destroy।
Shallow copy vs deep copyShallow copy vs deep copy
The compiler's free (default) copy constructor copies members one by one. If a member is a pointer, only the address is copied — both objects then point to the same memory. That is a shallow copy. Dangerous: when one object's destructor frees that memory, the other object holds a dangling pointer, and freeing again causes a double-free crash.
A deep copy allocates new memory and copies the actual data. You must write the copy constructor yourself for this.
Compiler-এর free (default) copy constructor member-গুলো একটা একটা করে copy করে। কোনো member যদি pointer হয়, শুধু address-টা copy হয় — তখন দুই object একই memory-কে point করে। এটাই shallow copy। বিপদ: এক object-এর destructor ওই memory free করলে অন্য object-এর কাছে dangling pointer থেকে যায়, আবার free করলে double-free crash।
Deep copy নতুন memory নিয়ে আসল data copy করে। এর জন্য copy constructor নিজে লিখতে হয়।
class MyArray {
int *data;
int n;
public:
MyArray(int n) {
this->n = n;
data = new int[n];
}
// DEEP copy constructor
MyArray(const MyArray &other) {
n = other.n;
data = new int[n]; // new memory!
for (int i = 0; i < n; i++)
data[i] = other.data[i]; // copy real values
}
~MyArray() { delete[] data; }
};
int main() {
MyArray a(5);
MyArray b = a; // deep copy: b gets its own memory
} // both destructors run safely — no double free
| Shallow copyShallow copy | Deep copyDeep copy | |
|---|---|---|
| What is copiedকী copy হয় | Member values only (pointer = address)শুধু member value (pointer = address) | Actual data, in new memoryআসল data, নতুন memory-তে |
| Who provides itকে দেয় | Compiler defaultCompiler-এর default | You write it yourselfনিজে লিখতে হয় |
| Riskঝুঁকি | Dangling pointer, double freeDangling pointer, double free | Safe (a bit slower)নিরাপদ (একটু slow) |
new memory). BUET-style questions often show a class with new in the constructor and no copy constructor, then ask what goes wrong when you copy the object.
Rule of Three: কোনো class-এর যদি custom destructor, copy constructor বা copy assignment operator-এর একটা লাগে, তাহলে প্রায় সবসময় তিনটাই লাগে (যখন class কোনো resource manage করে, যেমন new দিয়ে নেওয়া memory)। BUET-style প্রশ্নে প্রায়ই constructor-এ new আছে কিন্তু copy constructor নেই — এমন class দেখিয়ে জিজ্ঞেস করে object copy করলে কী সমস্যা হবে।
4. Inheritance4. Inheritance
Inheritance lets a new class (derived / child class) reuse the members of an existing class (base / parent class). The child gets everything the parent has (except private access), and can add its own members or change behavior.
Syntax: class Derived : public Base { ... };
Main benefit: code reuse, plus it enables runtime polymorphism (next section).
Inheritance দিয়ে একটা নতুন class (derived / child class) আগের একটা class-এর (base / parent class) member-গুলো reuse করতে পারে। Child তার parent-এর সবকিছু পায় (private access ছাড়া), আর চাইলে নিজের member যোগ করতে বা behavior বদলাতে পারে।
Syntax: class Derived : public Base { ... };
মূল লাভ: code reuse, আর এটাই runtime polymorphism-এর দরজা খোলে (পরের section)।
Five types of inheritanceInheritance-এর পাঁচ ধরন
| TypeType | Meaningমানে | ExampleExample |
|---|---|---|
| SingleSingle | One base → one derivedএকটা base → একটা derived | B : public A |
| MultipleMultiple | One derived from two or more basesদুই বা বেশি base থেকে একটা derived | C : public A, public B |
| MultilevelMultilevel | Chain: grandparent → parent → childChain: grandparent → parent → child | B : A, thenতারপর C : B |
| HierarchicalHierarchical | Many derived from one baseএক base থেকে অনেক derived | B : A, C : A |
| HybridHybrid | Mix of two or more types (often makes a diamond)দুই বা বেশি ধরনের মিশ্রণ (প্রায়ই diamond তৈরি হয়) | Multiple + hierarchicalMultiple + hierarchical |
class Animal {
public:
Animal() { cout << "Animal made\n"; }
~Animal() { cout << "Animal gone\n"; }
void eat() { cout << "eating\n"; }
};
class Dog : public Animal {
public:
Dog() { cout << "Dog made\n"; }
~Dog() { cout << "Dog gone\n"; }
void bark() { cout << "woof\n"; }
};
int main() {
Dog d;
d.eat(); // inherited from Animal
d.bark(); // Dog's own
}
// Output:
// Animal made <- base constructor FIRST
// Dog made
// eating
// woof
// Dog gone <- derived destructor FIRST
// Animal gone
Access modes of inheritanceInheritance-এর access mode
The word after the colon (: public / : protected / : private) sets how the base members appear inside the derived class. Rule of thumb: the base member's access becomes the more restrictive of its own access and the inheritance mode. Private members of the base are never directly accessible in the derived class.
Colon-এর পরের word-টা (: public / : protected / : private) ঠিক করে base-এর member-গুলো derived class-এ কেমন হয়ে ঢুকবে। সহজ নিয়ম: base member-এর access আর inheritance mode — দুটোর মধ্যে যেটা বেশি কড়া, সেটাই হয়। Base-এর private member derived class থেকে কখনোই সরাসরি ধরা যায় না।
| Base member ↓ / Mode →Base member ↓ / Mode → | public inheritanceinheritance |
protected inheritanceinheritance |
private inheritanceinheritance |
|---|---|---|---|
public |
public | protected | private |
protected |
protected | protected | private |
private |
not accessibleধরা যায় না | not accessibleধরা যায় না | not accessibleধরা যায় না |
The diamond problem and virtual base classDiamond problem আর virtual base class
In hybrid inheritance, a diamond shape can appear: B and C both inherit from A, and D inherits from both B and C. Now D contains two copies of A's members. Calling an A member through D becomes ambiguous — the compiler does not know which copy you mean.
Fix: make A a virtual base class: class B : virtual public A and class C : virtual public A. Then D keeps only one shared copy of A.
Hybrid inheritance-এ একটা diamond shape তৈরি হতে পারে: B আর C দুজনেই A থেকে inherit করে, আর D inherit করে B আর C দুজন থেকে। তখন D-এর ভেতরে A-এর member-দের দুইটা copy থাকে। D দিয়ে A-এর member call করলে ambiguous হয় — compiler বোঝে না কোন copy-টা চাইছেন।
সমাধান: A-কে virtual base class বানান: class B : virtual public A আর class C : virtual public A। তখন D-তে A-এর একটাই shared copy থাকে।
class A { public: int x; };
// WITHOUT virtual: D gets TWO x's
class B : public A {};
class C : public A {};
class D : public B, public C {};
int main() {
D d;
// d.x = 5; // ERROR: ambiguous (B::x or C::x?)
d.B::x = 5; // must pick a path — ugly
}
// WITH virtual: D gets ONE shared x
class B2 : virtual public A {};
class C2 : virtual public A {};
class D2 : public B2, public C2 {};
int main2() {
D2 d;
d.x = 5; // OK now — only one copy of A
return 0;
}
D2) calls the virtual base's constructor directly. (4) Java avoids the diamond problem by not allowing multiple inheritance of classes.
পরীক্ষার point: (১) Constructor চলে base → derived; destructor চলে derived → base। (২) Constructor আর destructor inherit হয় না; friend-ও inherit হয় না। (৩) Virtual base class থাকলে most derived class (এখানে D2) সরাসরি virtual base-এর constructor call করে। (৪) Java class-এর multiple inheritance allow করে না বলে diamond problem এড়ায়।
5. Polymorphism5. Polymorphism
Polymorphism means "many forms" — the same name or the same call behaves differently in different situations. C++ has two kinds:
- Compile-time (static): the compiler decides which function runs. Done by function overloading and operator overloading. Also called early binding.
- Runtime (dynamic): the decision happens while the program runs. Done by virtual functions through base-class pointers or references. Also called late binding.
Polymorphism মানে "many forms" — একই নাম বা একই call ভিন্ন পরিস্থিতিতে ভিন্নভাবে কাজ করে। C++ এ দুই ধরন:
- Compile-time (static): কোন function চলবে তা compiler ঠিক করে। হয় function overloading আর operator overloading দিয়ে। এটাকে early binding-ও বলে।
- Runtime (dynamic): সিদ্ধান্তটা program চলার সময় হয়। হয় base-class pointer বা reference দিয়ে virtual function call করলে। এটাকে late binding বলে।
Function overloading (compile-time)Function overloading (compile-time)
Same function name, different parameter list (different number or types). Return type alone is not enough to overload.
একই function name, কিন্তু আলাদা parameter list (সংখ্যা বা type আলাদা)। শুধু return type আলাদা হলে overload হয় না।
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c){ return a + b + c; }
int main() {
cout << add(2, 3) << endl; // 5 (int version)
cout << add(2.5, 3.5) << endl; // 6 (double version)
cout << add(1, 2, 3) << endl; // 6 (3-arg version)
}
Operator overloading (compile-time)Operator overloading (compile-time)
You can teach operators like +, ==, << to work on your own classes. Some operators can never be overloaded: ::, ., .*, ?:, sizeof.
+, ==, << — এই operator-গুলোকে নিজের class-এর জন্য কাজ করানো যায়। কিছু operator কখনো overload করা যায় না: ::, ., .*, ?:, sizeof।
class Complex {
public:
double re, im;
Complex(double r, double i) : re(r), im(i) {}
Complex operator+(const Complex &o) {
return Complex(re + o.re, im + o.im);
}
};
int main() {
Complex a(1, 2), b(3, 4);
Complex c = a + b; // calls a.operator+(b)
cout << c.re << "+" << c.im << "i";
}
// Output: 4+6i
Virtual functions (runtime)Virtual functions (runtime)
Situation: a base-class pointer holds a derived object. Which function runs — base's or derived's?
- Non-virtual: the pointer type decides → base version runs (early binding).
- virtual: the actual object decides → derived version runs (late binding).
Redefining a virtual function in a derived class with the same signature is called overriding.
পরিস্থিতি: একটা base-class pointer একটা derived object ধরে আছে। কোন function চলবে — base-এরটা নাকি derived-এরটা?
- Non-virtual: pointer-এর type ঠিক করে → base-এর version চলে (early binding)।
- virtual: আসল object ঠিক করে → derived-এর version চলে (late binding)।
Derived class-এ একই signature দিয়ে virtual function আবার লেখাকে বলে overriding।
class Base {
public:
void hello() { cout << "Base hello\n"; } // non-virtual
virtual void greet() { cout << "Base greet\n"; } // virtual
};
class Derived : public Base {
public:
void hello() { cout << "Derived hello\n"; } // hides Base::hello
void greet() override { cout << "Derived greet\n"; } // overrides
};
int main() {
Derived d;
Base *p = &d; // base pointer → derived object
p->hello(); // Base hello (non-virtual: pointer type wins)
p->greet(); // Derived greet (virtual: object type wins)
d.hello(); // Derived hello (direct call, no pointer)
d.greet(); // Derived greet
}
// Output:
// Base hello
// Derived greet
// Derived hello
// Derived greet
Memory trick: virtual → look at the object; non-virtual → look at the pointer type. মনে রাখার কৌশল: virtual → object দেখুন; non-virtual → pointer-এর type দেখুন।
How it works: the vtable ideaকীভাবে কাজ করে: vtable-এর idea
When a class has at least one virtual function, the compiler builds a hidden table of function pointers for it — the vtable. Every object of that class carries a hidden pointer — the vptr — pointing to its class's vtable. A virtual call goes: object → vptr → vtable → correct function. That lookup happens at runtime, so the actual object type always wins. Cost: one extra pointer per object and one indirection per call.
কোনো class-এ অন্তত একটা virtual function থাকলে compiler তার জন্য function pointer-দের একটা লুকানো table বানায় — এটাই vtable। ওই class-এর প্রতিটা object-এর ভেতরে একটা লুকানো pointer থাকে — vptr — যেটা তার class-এর vtable-কে point করে। Virtual call যায় এভাবে: object → vptr → vtable → সঠিক function। এই lookup runtime-এ হয়, তাই আসল object-এর type-ই জেতে। খরচ: object প্রতি একটা extra pointer আর call প্রতি একটা indirection।
Pure virtual functions and abstract classesPure virtual function আর abstract class
- A pure virtual function has no body in the base:
virtual double area() = 0; - A class with at least one pure virtual function is an abstract class. You cannot create its object, but you can have pointers/references to it.
- A derived class must override all pure virtual functions, or it stays abstract too.
- Pure virtual function-এর base-এ কোনো body থাকে না:
virtual double area() = 0; - যে class-এ অন্তত একটা pure virtual function আছে, সেটা abstract class। এর object বানানো যায় না, কিন্তু pointer/reference রাখা যায়।
- Derived class-কে সবগুলো pure virtual function override করতে হয়, নাহলে সেও abstract থেকে যায়।
class Shape {
public:
virtual double area() = 0; // pure virtual
virtual ~Shape() {} // virtual destructor!
};
class Circle : public Shape {
double r;
public:
Circle(double r) : r(r) {}
double area() override { return 3.1416 * r * r; }
};
class Rect : public Shape {
double w, h;
public:
Rect(double w, double h) : w(w), h(h) {}
double area() override { return w * h; }
};
int main() {
Shape *shapes[2] = { new Circle(1), new Rect(2, 3) };
for (int i = 0; i < 2; i++)
cout << shapes[i]->area() << endl; // right area() picked at runtime
for (int i = 0; i < 2; i++)
delete shapes[i]; // safe: destructor is virtual
}
// Output:
// 3.1416
// 6
delete a derived object through a base pointer and the base destructor is not virtual, only the base destructor runs — the derived part is never cleaned up (memory leak / undefined behavior). Rule: any class meant to be a polymorphic base must have a virtual destructor. Also: constructors can never be virtual.
Virtual destructor — পরীক্ষার top favorite। Base pointer দিয়ে derived object delete করলে, base destructor virtual না হলে শুধু base-এর destructor চলে — derived অংশটা কখনো cleanup হয় না (memory leak / undefined behavior)। নিয়ম: যে class polymorphic base হবে, তার destructor অবশ্যই virtual হতে হবে। আরও মনে রাখুন: constructor কখনো virtual হতে পারে না।
Overloading vs overriding vs hidingOverloading vs overriding vs hiding
| OverloadingOverloading | OverridingOverriding | HidingHiding | |
|---|---|---|---|
| Whereকোথায় | Same class (same scope)একই class-এ (একই scope) | Base + derived classBase + derived class-এ | Base + derived classBase + derived class-এ |
| SignatureSignature | Same name, different parametersএকই নাম, আলাদা parameter | Same name, same parameters, base is virtualএকই নাম, একই parameter, base-এ virtual | Same name, base not virtual (or different parameters)একই নাম, base-এ virtual নেই (বা parameter আলাদা) |
| Decidedকখন ঠিক হয় | Compile timeCompile time-এ | Runtime (via vtable)Runtime-এ (vtable দিয়ে) | Compile timeCompile time-এ |
| Through base pointerBase pointer দিয়ে | — | Derived version runsDerived version চলে | Base version runsBase version চলে |
| Compile-time polymorphismCompile-time polymorphism | Runtime polymorphismRuntime polymorphism | |
|---|---|---|
| Other namesঅন্য নাম | Static / early bindingStatic / early binding | Dynamic / late bindingDynamic / late binding |
| Achieved byকী দিয়ে হয় | Function + operator overloading, templatesFunction + operator overloading, templates | Virtual functions + base pointer/referenceVirtual function + base pointer/reference |
| Speedগতি | Faster (direct call)দ্রুত (direct call) | Slightly slower (vtable lookup)একটু slow (vtable lookup) |
| FlexibilityFlexibility | Fixed at compile timeCompile time-এই fixed | Decided by actual object at runtimeRuntime-এ আসল object ঠিক করে |
6. Other C++ Concepts6. Other C++ Concepts
Static membersStatic members
A static data member is shared by all objects — there is only one copy for the whole class. It must be defined once outside the class. A static member function can be called without any object (ClassName::func()) and can only touch static members — it has no this.
static data member সব object মিলে share করে — পুরো class-এর জন্য একটাই copy থাকে। Class-এর বাইরে একবার define করতে হয়। static member function কোনো object ছাড়াই call করা যায় (ClassName::func()), আর সে শুধু static member ধরতে পারে — তার কোনো this নেই।
class Counter {
public:
static int count; // declaration (shared)
Counter() { count++; }
static int get() { return count; } // no object needed
};
int Counter::count = 0; // definition (required, outside)
int main() {
Counter a, b, c;
cout << Counter::get(); // 3 — one shared count
}
// Output: 3
Friend function and friend classFriend function আর friend class
A friend function is not a member, but the class gives it permission to access its private and protected members. A friend class gets the same permission for all its functions. Important facts: friendship is not mutual (A friending B does not make B friend A), not inherited, and not transitive.
Friend function class-এর member না, কিন্তু class তাকে নিজের private আর protected member ধরার অনুমতি দেয়। Friend class হলে তার সব function সেই অনুমতি পায়। জরুরি কথা: friendship mutual না (A যদি B-কে friend বানায়, B automatic A-এর friend হয় না), inherit হয় না, আর transitive-ও না।
class Box {
private:
int secret = 42;
friend void peek(Box &b); // friend declaration
};
void peek(Box &b) { // normal function, not a member
cout << b.secret; // allowed! it is a friend
}
int main() {
Box b;
peek(b); // 42
}
Templates (generic programming)Templates (generic programming)
A template lets you write one function or class that works for any type. The compiler generates a separate version for each type you use — that is why templates are compile-time polymorphism.
Template দিয়ে এমন একটা function বা class লেখা যায় যেটা যেকোনো type-এর জন্য কাজ করে। আপনি যে যে type ব্যবহার করেন, compiler প্রতিটার জন্য আলাদা version বানিয়ে নেয় — এজন্যই template compile-time polymorphism।
template <typename T>
T maxOf(T a, T b) { return (a > b) ? a : b; }
template <typename T>
class Pair {
public:
T first, second;
Pair(T a, T b) : first(a), second(b) {}
};
int main() {
cout << maxOf(3, 7) << endl; // 7 (T = int)
cout << maxOf(2.5, 1.5) << endl; // 2.5 (T = double)
Pair<int> p(1, 2);
cout << p.first + p.second; // 3
}
Exception handlingException handling
Exceptions separate error handling from normal code. throw raises an error, try marks the risky block, catch handles it. catch (...) catches anything. When an exception is thrown, the stack "unwinds": local objects are destroyed properly on the way to the matching catch.
Exception দিয়ে error handle করার code-কে normal code থেকে আলাদা রাখা যায়। throw error ছোড়ে, try ঝুঁকির block-টা ঘিরে রাখে, catch সেটা ধরে। catch (...) সবকিছু ধরে। Exception throw হলে stack "unwind" হয়: matching catch-এ পৌঁছানোর পথে local object-গুলো ঠিকভাবে destroy হয়।
double divide(double a, double b) {
if (b == 0) throw string("Divide by zero!");
return a / b;
}
int main() {
try {
cout << divide(10, 2) << endl; // 5
cout << divide(7, 0) << endl; // throws — skips the cout
cout << "never printed\n";
}
catch (string &msg) {
cout << "Error: " << msg << endl;
}
cout << "Program continues\n";
}
// Output:
// 5
// Error: Divide by zero!
// Program continues
STL quick overview: vector, map, setSTL quick overview: vector, map, set
The STL (Standard Template Library) gives ready-made containers and algorithms, all built with templates. Three you must know:
STL (Standard Template Library) ready-made container আর algorithm দেয়, সবই template দিয়ে বানানো। এই তিনটা জানতেই হবে:
| ContainerContainer | What it isএটা কী | Order / duplicatesOrder / duplicate | Key operationsKey operations |
|---|---|---|---|
vector |
Dynamic (growable) arrayDynamic (বাড়ানো যায় এমন) array | Insertion order; duplicates OKInsertion order; duplicate চলে | push_back O(1) amortized, index access O(1)push_back O(1) amortized, index access O(1) |
map |
Key → value pairs (balanced BST inside)Key → value pair (ভেতরে balanced BST) | Sorted by key; keys uniqueKey অনুযায়ী sorted; key unique | insert / find / erase — O(log n)insert / find / erase — O(log n) |
set |
Collection of unique valuesUnique value-দের collection | Sorted; no duplicatesSorted; duplicate নেই | insert / find / erase — O(log n)insert / find / erase — O(log n) |
#include <vector>
#include <map>
#include <set>
int main() {
vector<int> v = {3, 1, 3};
v.push_back(5); // v = 3 1 3 5 (duplicates kept)
set<int> s(v.begin(), v.end()); // s = 1 3 5 (sorted, unique)
map<string, int> marks;
marks["math"] = 80;
marks["cse"] = 95;
cout << marks["cse"] << endl; // 95
for (int x : s) cout << x << " "; // 1 3 5
}
// Output:
// 95
// 1 3 5
this pointer. (2) friend functions are declared inside the class but are not members and are not affected by access specifiers. (3) map/set are usually implemented as red-black trees → O(log n); unordered_map uses hashing → average O(1). (4) Throwing an exception from a destructor is dangerous — avoid it.
Quick পরীক্ষা-fact: (১) static member function-এর this pointer নেই। (২) friend function class-এর ভেতরে declare হয় কিন্তু member না, access specifier-ও তার ওপর কাজ করে না। (৩) map/set সাধারণত red-black tree দিয়ে বানানো → O(log n); unordered_map hashing ব্যবহার করে → average O(1)। (৪) Destructor থেকে exception throw করা বিপজ্জনক — এড়িয়ে চলুন।
Standard C++ threads: std::threadStandard C++ threads: std::thread
Since C++11, threads are part of the language's standard library: std::thread (header <thread>). You give the constructor a function (plus its arguments), and that function runs in a new worker thread. The main thread must call join() to wait for the worker to finish before using its result.
C++11 থেকে thread language-এর standard library-র অংশ: std::thread (header <thread>)। Constructor-এ একটা function (আর তার argument) দিলে সেই function নতুন একটা worker thread-এ চলে। Worker-এর result ব্যবহারের আগে main thread-কে join() call করে তার শেষ হওয়া পর্যন্ত অপেক্ষা করতে হয়।
#include <iostream>
#include <thread>
using namespace std;
unsigned long long fact = 1;
void factorial(int n) { // this runs in the worker thread
for (int i = 2; i <= n; i++)
fact *= i;
}
int main() {
thread t(factorial, 5); // start worker thread, argument 5
t.join(); // wait for the worker to finish
cout << "5! = " << fact << endl;
}
// compile: g++ -std=c++11 fact.cpp -pthread
// Output:
// 5! = 120
Without join(), main could end (or print) before the worker finishes — that is a bug. join() is the "wait here" line.
যদি join() না দেন, worker শেষ হওয়ার আগেই main শেষ হয়ে (বা print করে) যেতে পারে — সেটা bug। join() মানেই "এখানে দাঁড়িয়ে অপেক্ষা করো"।
pthread is an old C library (pthread_create with void* casts, Unix-only style), while std::thread is standard C++11 — an object, type-safe, takes any function with normal arguments, and is portable.
October 2017-এ এসেছিল — আর প্রশ্নে স্পষ্ট বলা ছিল standard C++ threads ব্যবহার করতে, pthread না। এক লাইনে পার্থক্য: pthread পুরনো C library (pthread_create, void* cast, Unix-ঘেঁষা), আর std::thread হলো standard C++11 — একটা object, type-safe, normal argument-সহ যেকোনো function নেয়, আর portable।
Java Corner (Real Exam Questions!)Java Corner (Real Exam Questions!)
The official syllabus says OOP with C++. But in real BUET exams, Java questions appear too! So learn these few Java things well. They are short, and they were actually asked. Official syllabus-এ লেখা OOP with C++। কিন্তু আসল BUET পরীক্ষায় Java-র প্রশ্নও আসে! তাই এই কয়েকটা Java বিষয় ভালো করে শিখে রাখুন। এগুলো ছোট, আর সত্যিই পরীক্ষায় এসেছে।
Java vs C++ — quick differencesJava vs C++ — দ্রুত পার্থক্য
| Thingবিষয় | C++ | Java |
|---|---|---|
| PointersPointer | Yes, full pointer arithmeticআছে, full pointer arithmetic | No pointers — only referencesPointer নেই — শুধু reference |
| Memory cleanupMemory cleanup | Manual: new / deleteManual: new / delete |
Automatic garbage collection (GC)Automatic garbage collection (GC) |
| Where code livesCode কোথায় থাকে | Free functions allowed (like main)Class-এর বাইরে free function লেখা যায় (যেমন main) |
Everything must be inside a classসবকিছু class-এর ভেতরে থাকতেই হবে |
| Multiple inheritanceMultiple inheritance | Yes, of classes (diamond problem!)Class-এর multiple inheritance আছে (diamond problem!) | Not for classes — use interfaces instead (a class can implement many)Class-এ নেই — বদলে interface ব্যবহার হয় (একটা class অনেকগুলো implement করতে পারে) |
| Runs onচলে কীসে | Compiled to native machine codeসরাসরি native machine code-এ compile হয় | Bytecode on the JVM ("write once, run anywhere")JVM-এ bytecode হিসেবে ("write once, run anywhere") |
| Operator overloadingOperator overloading | Yesআছে | No (only + for String is built in)নেই (শুধু String-এর + built-in) |
String pool: == vs .equals()String pool: == vs .equals()
In Java, == on objects compares references (same object in memory?). .equals() compares the content. String literals go into a shared "string pool", so equal literals point to the same object. But new String("...") always makes a brand new object outside the pool.
Java-তে object-এর ওপর == তুলনা করে reference (memory-তে একই object কি না?)। .equals() তুলনা করে content। String literal-গুলো একটা shared "string pool"-এ থাকে, তাই সমান literal একই object-কে point করে। কিন্তু new String("...") সবসময় pool-এর বাইরে একদম নতুন object বানায়।
String a = "buet";
String b = "buet"; // same pool object as a
String c = new String("buet"); // NEW object, outside the pool
System.out.println(a == b); // true (same pool reference)
System.out.println(a == c); // false (different objects)
System.out.println(a.equals(c)); // true (same content "buet")
Output: true, false, true. Rule of thumb: always compare Strings with .equals(), never with ==.
Output: true, false, true। মনে রাখার নিয়ম: String তুলনা সবসময় .equals() দিয়ে, কখনোই == দিয়ে না।
String is immutableString হলো immutable
A Java String can never change after it is made. Methods like concat, toUpperCase, replace do NOT change the old String — they return a new String object. If you ignore the returned value, nothing seems to happen.
Java String একবার বানানোর পর কখনো বদলায় না। concat, toUpperCase, replace — এই method-গুলো পুরনো String বদলায় না — একটা নতুন String object return করে। Return value না ধরলে মনে হবে কিছুই হয়নি।
String s = "BUET";
s.concat(" CSE"); // makes a new String, but we threw it away!
System.out.println(s); // BUET (s did not change)
s = s.concat(" CSE"); // now we keep the new object
System.out.println(s); // BUET CSE
Output: BUET then BUET CSE. For heavy string building, use StringBuilder — it IS mutable.
Output: BUET তারপর BUET CSE। অনেক string জোড়া দিতে হলে StringBuilder ব্যবহার করুন — সেটা mutable।
Nested classes: static vs non-static (inner)Nested class: static vs non-static (inner)
A class written inside another class is a nested class. A static nested class does not need an outer object — create it directly. A non-static nested class (called an inner class) belongs to an outer object, so you must have an outer object first, and use the special syntax outer.new Inner().
এক class-এর ভেতরে লেখা class-কে nested class বলে। static nested class-এর জন্য outer object লাগে না — সরাসরি বানানো যায়। non-static nested class (যাকে inner class বলে) একটা outer object-এর সাথে যুক্ত, তাই আগে outer object লাগবেই, আর বিশেষ syntax outer.new Inner() ব্যবহার করতে হয়।
class Outer {
int x = 10;
class Inner { // non-static inner class
void show() { System.out.println(x); } // can use outer's x
}
static class Helper { // static nested class
void hi() { System.out.println("hi"); }
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer(); // 1) outer object first
Outer.Inner in = outer.new Inner(); // 2) special syntax!
in.show(); // 10
Outer.Helper h = new Outer.Helper(); // static: no outer object
h.hi(); // hi
}
}
Key line: Outer.Inner in = outer.new Inner(); — the inner object is tied to that specific outer object and can read its fields (like x).
Key line: Outer.Inner in = outer.new Inner(); — inner object-টা ওই নির্দিষ্ট outer object-এর সাথে বাঁধা থাকে, আর তার field (যেমন x) পড়তে পারে।
Functional interface and lambdaFunctional interface আর lambda
A functional interface is an interface with exactly one abstract method. That is the whole definition. Because there is only one method, Java can let you write its body as a short lambda expression — no class, no method name, just (parameters) -> body. You can mark the interface with @FunctionalInterface; then the compiler gives an error if someone adds a second abstract method. (Default and static methods are allowed — they do not count, because they already have bodies.)
Functional interface হলো এমন interface যাতে ঠিক একটা abstract method থাকে। Definition এটুকুই। Method যেহেতু একটাই, Java তার body ছোট একটা lambda expression হিসেবে লিখতে দেয় — কোনো class না, method-এর নামও না, শুধু (parameters) -> body। Interface-টাকে @FunctionalInterface দিয়ে mark করা যায়; তখন কেউ দ্বিতীয় abstract method যোগ করলে compiler error দেয়। (Default আর static method থাকতে পারে — ওগুলো গোনা হয় না, কারণ ওদের body আগে থেকেই আছে।)
Java already ships many built-in functional interfaces. Learn these four names: Java-তে অনেক built-in functional interface আগে থেকেই আছে। এই চারটা নাম শিখে রাখুন:
| InterfaceInterface | Its one abstract methodতার একমাত্র abstract method | Meaningমানে |
|---|---|---|
Runnable |
void run() |
A task to run (no input, no output)চালানোর মতো একটা task (input নেই, output নেই) |
Comparator<T> |
int compare(T a, T b) |
Which of two objects comes first (for sorting)দুই object-এর কোনটা আগে আসবে (sorting-এর জন্য) |
Function<T,R> |
R apply(T t) |
Turn a T into an Rএকটা T থেকে একটা R বানানো |
Predicate<T> |
boolean test(T t) |
A yes/no check on a Tএকটা T-এর ওপর yes/no check |
@FunctionalInterface
interface Calculator {
int operate(int a, int b); // the ONE abstract method
}
public class Main {
public static void main(String[] args) {
Calculator add = (a, b) -> a + b; // lambda = the method body
Calculator mul = (a, b) -> a * b;
System.out.println(add.operate(3, 4)); // 7
System.out.println(mul.operate(3, 4)); // 12
}
}
The lambda (a, b) -> a + b is a short way to implement operate. Java knows which method it fills in — there is only one.
Lambda (a, b) -> a + b আসলে operate implement করার ছোট রাস্তা। কোন method-এর body এটা, Java জানে — কারণ method তো একটাই।
@FunctionalInterface + a built-in name (Runnable) + a small lambda like the one above.
April 2024-এ এসেছিল: "Java-তে functional interface কী? উদাহরণ দিন।" নিরাপদ full answer: একটা abstract method + @FunctionalInterface + একটা built-in নাম (Runnable) + ওপরের মতো ছোট একটা lambda।
Lambda + Streams: shorter, loop-free codeLambda + Streams: ছোট, loop ছাড়া code
Before Java 8, passing "a piece of behavior" needed an anonymous inner class — five lines of ceremony. A lambda says the same thing in one line. Classic GUI example: a button click handler (ActionListener is a functional interface, so a lambda fits).
Java 8-এর আগে "এক টুকরো behavior" pass করতে anonymous inner class লাগত — পাঁচ লাইনের আনুষ্ঠানিকতা। Lambda একই কথা এক লাইনে বলে। Classic GUI উদাহরণ: button click handler (ActionListener একটা functional interface, তাই lambda বসানো যায়)।
// OLD: anonymous inner class (before Java 8)
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked!");
}
});
// NEW: lambda (Java 8+) — same meaning, one line
button.addActionListener(e -> System.out.println("Clicked!"));
Both create an object whose one method prints "Clicked!". The lambda works because ActionListener has exactly one abstract method (actionPerformed).
দুটোই এমন object বানায় যার একমাত্র method "Clicked!" print করে। Lambda চলে কারণ ActionListener-এর ঠিক একটাই abstract method (actionPerformed)।
A Stream is a pipeline over a collection: you describe what to do (filter, map, collect), and Java does the looping for you. Exam favorite: process a list without writing any loop.
Stream হলো collection-এর ওপর একটা pipeline: আপনি বলেন কী করতে হবে (filter, map, collect), আর loop চালানোর কাজটা Java নিজে করে। পরীক্ষার প্রিয় প্রশ্ন: কোনো loop না লিখে একটা list process করা।
import java.util.*;
import java.util.stream.*;
List<String> words = Arrays.asList("BUET", "CSE", "Admission");
List<Integer> lengths = words.stream() // 1) open the pipeline
.map(String::length) // 2) each word -> its length
.collect(Collectors.toList()); // 3) gather into a new list
System.out.println(lengths); // [4, 3, 9]
String::length is a method reference — an even shorter lambda; it means w -> w.length(). Bonus one-liners: words.stream().filter(w -> w.length() > 3).count() counts long words; words.forEach(System.out::println) prints them all.
String::length হলো method reference — আরও ছোট lambda; মানে w -> w.length()। Bonus এক-লাইনার: words.stream().filter(w -> w.length() > 3).count() লম্বা word গোনে; words.forEach(System.out::println) সবগুলো print করে।
stream() → map()/filter() → collect() chain above.
October 2017 আর October 2018-এ এসেছিল: anonymous-class listener-কে lambda দিয়ে নতুন করে লেখা, আর loop-এর বদলে stream দিয়ে list process করা। প্রশ্নে যদি বলে "loop ব্যবহার করা যাবে না", expected answer হলো ওপরের stream() → map()/filter() → collect() chain।
new vs literals (== vs .equals(), string pool), (2) how to create an object of a non-static inner class (outer.new Inner()), (3) String immutability behavior. The syllabus says C++, but revise this Java corner before the exam!
BUET April 2019 পরীক্ষায় ঠিক এই জিনিসগুলোই এসেছিল: (১) new vs literal দিয়ে বানানো String তুলনার output (== vs .equals(), string pool), (২) non-static inner class-এর object কীভাবে বানায় (outer.new Inner()), (৩) String immutability-র আচরণ। Syllabus-এ C++ লেখা থাকলেও পরীক্ষার আগে এই Java corner-টা অবশ্যই revise করুন!
Practice Questions (Admission Style)Practice Questions (Admission Style)
class?
class-এ member-দের default access specifier কোনটা?
Show Answerউত্তর দেখুন
class, members are private by default. In a struct, they are public by default. This is the only real difference between struct and class in C++.class-এ member-রা default-এ private। struct-এ default-এ public। C++ এ struct আর class-এর আসল পার্থক্য এটাই।Show Answerউত্তর দেখুন
Show Answerউত্তর দেখুন
class A {
int id;
public:
A(int i) { id = i; cout << "C" << id; }
~A() { cout << "D" << id; }
};
int main() {
A p(1);
{ A q(2); }
A r(3);
}
class A {
int id;
public:
A(int i) { id = i; cout << "C" << id; }
~A() { cout << "D" << id; }
};
int main() {
A p(1);
{ A q(2); }
A r(3);
}
Show Answerউত্তর দেখুন
q lives inside an inner block { }, so it dies as soon as the block ends: C1, C2, D2. Then r is made: C3. At the end of main, destruction is reverse of creation among the living objects (r then p): D3, D1. Full output: C1C2D2C3D3D1.q ভেতরের block { }-এ আছে, তাই block শেষ হতেই সে destroy হয়: C1, C2, D2। তারপর r তৈরি হয়: C3। main শেষে বেঁচে থাকা object-রা তৈরির উল্টা order-এ destroy হয় (আগে r, পরে p): D3, D1। পুরো output: C1C2D2C3D3D1।Show Answerউত্তর দেখুন
Point b = a;), (2) an object is passed to a function by value, (3) an object is returned from a function by value. The parameter must be a reference (const Point &) because if it were pass-by-value, that very pass would need a copy — which calls the copy constructor again, and again — infinite recursion. The compiler rejects a by-value copy constructor.Point b = a;), (২) object কোনো function-এ by value pass হয়, (৩) function থেকে object by value return হয়। Parameter reference (const Point &) হতেই হবে, কারণ value হলে ওই pass করতেই একটা copy লাগত — তাতে আবার copy constructor call হতো, আবার হতো — infinite recursion। তাই compiler by-value copy constructor allow করে না।D is derived as class D : protected B. A public member of B becomes what inside D?
class D : protected B — এভাবে derive করা হলো। B-এর একটা public member D-এর ভেতরে কী হয়ে যায়?
Show Answerউত্তর দেখুন
Show Answerউত্তর দেখুন
ClassName::func(). It has no this pointer, so it cannot touch non-static members directly — (a), (b), (d) are all false.ClassName::func() দিয়েই call করা যায়। এর this pointer নেই, তাই non-static member সরাসরি ধরতে পারে না — (a), (b), (d) সবগুলোই মিথ্যা।class Base {
public:
virtual void f() { cout << "Bf "; }
void g() { cout << "Bg "; }
};
class Der : public Base {
public:
void f() { cout << "Df "; }
void g() { cout << "Dg "; }
};
int main() {
Der d;
Base *p = &d;
p->f(); p->g(); d.g();
}
class Base {
public:
virtual void f() { cout << "Bf "; }
void g() { cout << "Bg "; }
};
class Der : public Base {
public:
void f() { cout << "Df "; }
void g() { cout << "Dg "; }
};
int main() {
Der d;
Base *p = &d;
p->f(); p->g(); d.g();
}
Show Answerউত্তর দেখুন
f() is virtual, so through the base pointer the object's version runs: Df. g() is non-virtual, so the pointer type decides: Bg. Calling d.g() directly on the Der object gives Dg. Rule: virtual → object wins; non-virtual → pointer type wins.f() virtual, তাই base pointer দিয়েও object-এর version চলে: Df। g() virtual না, তাই pointer-এর type ঠিক করে: Bg। আর d.g() সরাসরি Der object-এ call, তাই Dg। নিয়ম: virtual → object জেতে; non-virtual → pointer type জেতে।Show Answerউত্তর দেখুন
int add(int,int); and double add(double,double);. Overriding: a derived class redefines a base class virtual function with the same signature; resolved at runtime through the vtable. Example: virtual void draw() in Shape, redefined as void draw() override in Circle; calling through a Shape* runs Circle's version. Key line: overloading = compile time + same class; overriding = runtime + inheritance + virtual.int add(int,int); আর double add(double,double);। Overriding: derived class base-এর virtual function একই signature দিয়ে আবার লেখে; runtime-এ vtable দিয়ে resolve হয়। Example: Shape-এ virtual void draw(), Circle-এ void draw() override; Shape* দিয়ে call করলে Circle-এর version চলে। মূল কথা: overloading = compile time + একই class; overriding = runtime + inheritance + virtual।Show Answerউত্তর দেখুন
d.x is ambiguous and A's constructor runs twice. Fix: declare class B : virtual public A and class C : virtual public A. Then D holds a single shared A subobject, the ambiguity disappears, and the most derived class (D) constructs A exactly once.d.x ambiguous, আর A-এর constructor দুইবার চলে। সমাধান: class B : virtual public A আর class C : virtual public A লিখুন। তখন D-তে A-এর একটাই shared subobject থাকে, ambiguity চলে যায়, আর most derived class (D) A-কে ঠিক একবার construct করে।new in its constructor and frees it in the destructor, but has no copy constructor. What exactly goes wrong when we write MyArray b = a; and then both objects are destroyed? What is the fix called?new দিয়ে memory নেয়, destructor-এ free করে, কিন্তু কোনো copy constructor নেই। MyArray b = a; লেখার পর দুই object destroy হলে ঠিক কী সমস্যা হয়? সমাধানটার নাম কী?Show Answerউত্তর দেখুন
a and b point to the same memory block. When b is destroyed, its destructor frees that block; a is left with a dangling pointer. When a is destroyed, it frees the same block again — a double free, which is undefined behavior (usually a crash). Fix: write a deep copy constructor that allocates new memory and copies element values (and, by the Rule of Three, also a matching assignment operator and destructor).a আর b একই memory block-কে point করে। b destroy হলে তার destructor block-টা free করে দেয়; a-এর কাছে থাকে dangling pointer। এরপর a destroy হলে একই block আবার free হয় — double free, যেটা undefined behavior (সাধারণত crash)। সমাধান: deep copy constructor লেখা, যেটা নতুন memory নিয়ে element-গুলোর value copy করে (আর Rule of Three অনুযায়ী matching assignment operator ও destructor-ও লাগবে)।class A {
public:
A() { cout << "A "; }
~A() { cout << "~A "; }
};
class B : public A {
public:
B() { cout << "B "; }
~B() { cout << "~B "; }
};
int main() {
B obj;
}
class A {
public:
A() { cout << "A "; }
~A() { cout << "~A "; }
};
class B : public A {
public:
B() { cout << "B "; }
~B() { cout << "~B "; }
};
int main() {
B obj;
}
Show Answerউত্তর দেখুন
A B. Destruction is the exact reverse, derived → base: ~B ~A. Think of it like building a house: foundation (base) first, roof last; demolition removes the roof first.A B। Destruction হয় ঠিক উল্টা, derived → base: ~B ~A। বাড়ি বানানোর মতো ভাবুন: আগে foundation (base), শেষে ছাদ; ভাঙার সময় আগে ছাদ।Show Answerউত্তর দেখুন
class Base {
public:
~Base() { cout << "~Base "; } // NOT virtual
};
class Der : public Base {
int *buf;
public:
Der() { buf = new int[100]; }
~Der() { delete[] buf; cout << "~Der "; }
};
int main() {
Base *p = new Der();
delete p; // prints only "~Base " !
}
Since ~Base() is non-virtual, delete p; uses the pointer's static type and calls only ~Base(). ~Der() never runs, so buf is never freed — memory leak, and formally undefined behavior. Making the base destructor virtual makes the delete go through the vtable: ~Der() runs first, then ~Base() — correct cleanup.class Base {
public:
~Base() { cout << "~Base "; } // virtual না
};
class Der : public Base {
int *buf;
public:
Der() { buf = new int[100]; }
~Der() { delete[] buf; cout << "~Der "; }
};
int main() {
Base *p = new Der();
delete p; // শুধু "~Base " ছাপে!
}
~Base() virtual না হওয়ায় delete p; pointer-এর static type দেখে শুধু ~Base() call করে। ~Der() কখনো চলে না, তাই buf কখনো free হয় না — memory leak, আর নিয়ম অনুযায়ী undefined behavior। Base destructor virtual করলে delete vtable দিয়ে যায়: আগে ~Der(), তারপর ~Base() — সঠিক cleanup।class Shape {
public:
virtual void draw() { cout << "Shape "; }
};
class Circle : public Shape {
public:
void draw() override { cout << "Circle "; }
};
void byValue(Shape s) { s.draw(); }
void byRef(Shape &s) { s.draw(); }
int main() {
Circle c;
byValue(c);
byRef(c);
}
class Shape {
public:
virtual void draw() { cout << "Shape "; }
};
class Circle : public Shape {
public:
void draw() override { cout << "Circle "; }
};
void byValue(Shape s) { s.draw(); }
void byRef(Shape &s) { s.draw(); }
int main() {
Circle c;
byValue(c);
byRef(c);
}
Show Answerউত্তর দেখুন
byValue(Shape s) copies only the Shape part of the Circle — this is object slicing. The parameter is a real Shape object, so s.draw() prints "Shape". byRef(Shape &s) keeps the original Circle object, so the virtual call prints "Circle". Lesson: runtime polymorphism works only through pointers or references, never by value.byValue(Shape s) Circle-এর শুধু Shape অংশটুকু copy করে — এটাই object slicing। Parameter-টা তখন আসল একটা Shape object, তাই s.draw() ছাপে "Shape"। byRef(Shape &s)-এ original Circle object-টাই থাকে, তাই virtual call ছাপে "Circle"। শিক্ষা: runtime polymorphism শুধু pointer বা reference দিয়েই কাজ করে, by value কখনো না।Employee with pure virtual salary(), and two derived classes — Regular (fixed monthly pay) and Hourly (rate × hours). In main, store both in an array of Employee* and print each salary using runtime polymorphism. Mention every place a virtual keyword is needed and why.Employee, যাতে pure virtual salary() আছে, আর দুইটা derived class — Regular (fixed monthly pay) আর Hourly (rate × hours)। main-এ দুটোকেই Employee*-এর array-তে রেখে runtime polymorphism দিয়ে প্রতিটার salary print করুন। কোথায় কোথায় virtual keyword লাগবে আর কেন — উল্লেখ করুন।Show Answerউত্তর দেখুন
class Employee {
public:
virtual double salary() = 0; // (1) pure virtual: makes class abstract
virtual ~Employee() {} // (2) virtual destructor: safe delete via Employee*
};
class Regular : public Employee {
double monthly;
public:
Regular(double m) : monthly(m) {}
double salary() override { return monthly; }
};
class Hourly : public Employee {
double rate, hours;
public:
Hourly(double r, double h) : rate(r), hours(h) {}
double salary() override { return rate * hours; }
};
int main() {
Employee *emps[2] = { new Regular(50000), new Hourly(500, 160) };
for (int i = 0; i < 2; i++)
cout << emps[i]->salary() << endl; // 50000, 80000
for (int i = 0; i < 2; i++) delete emps[i];
}
Virtual is needed in two places: (1) salary() = 0 — pure virtual so each derived class must give its own formula, and the call through Employee* picks the right one at runtime via the vtable; (2) the destructor — because we delete through an Employee*, without it the derived destructors would never run.class Employee {
public:
virtual double salary() = 0; // (১) pure virtual: class-টা abstract হয়
virtual ~Employee() {} // (২) virtual destructor: Employee* দিয়ে safe delete
};
class Regular : public Employee {
double monthly;
public:
Regular(double m) : monthly(m) {}
double salary() override { return monthly; }
};
class Hourly : public Employee {
double rate, hours;
public:
Hourly(double r, double h) : rate(r), hours(h) {}
double salary() override { return rate * hours; }
};
int main() {
Employee *emps[2] = { new Regular(50000), new Hourly(500, 160) };
for (int i = 0; i < 2; i++)
cout << emps[i]->salary() << endl; // 50000, 80000
for (int i = 0; i < 2; i++) delete emps[i];
}
Virtual লাগবে দুই জায়গায়: (১) salary() = 0 — pure virtual, যাতে প্রতিটা derived class নিজের formula লিখতে বাধ্য হয়, আর Employee* দিয়ে call করলে vtable দিয়ে runtime-এ সঠিকটা বাছাই হয়; (২) destructor-এ — কারণ আমরা Employee* দিয়ে delete করছি, virtual না হলে derived destructor-গুলো কখনোই চলত না।String a = "cse";
String b = "cse";
String c = new String("cse");
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(c));
String a = "cse";
String b = "cse";
String c = new String("cse");
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(c));
Show Answerউত্তর দেখুন
true, false, true. Why: (1) a == b is true — both literals come from the string pool, so a and b point to the very same object, and == compares references. (2) a == c is false — new String("cse") always creates a fresh object outside the pool, so the references differ even though the text is the same. (3) a.equals(c) is true — equals() compares the characters (content), and both hold "cse". Lesson: compare String content with .equals(), never ==.true, false, true। কারণ: (১) a == b হয় true — দুটো literal-ই string pool থেকে আসে, তাই a আর b একদম একই object-কে point করে, আর == reference তুলনা করে। (২) a == c হয় false — new String("cse") সবসময় pool-এর বাইরে নতুন object বানায়, তাই লেখা এক হলেও reference আলাদা। (৩) a.equals(c) হয় true — equals() character (content) তুলনা করে, আর দুটোতেই আছে "cse"। শিক্ষা: String content তুলনা .equals() দিয়ে, কখনোই == দিয়ে না।Inner is a non-static class written inside class Outer. Write the code to create an Inner object. (b) What does this code print, and why?
String s = "BUET";
s.concat(" CSE");
System.out.println(s);
Outer class-এর ভেতরে Inner একটা non-static class। Inner-এর object বানানোর code লিখুন। (b) নিচের code কী print করবে, আর কেন?
String s = "BUET";
s.concat(" CSE");
System.out.println(s);
Show Answerউত্তর দেখুন
Outer outer = new Outer();
Outer.Inner in = outer.new Inner();
Plain new Outer.Inner() does NOT compile for a non-static inner class — that form works only for a static nested class. (b) It prints BUET. String is immutable: concat(" CSE") does not change s — it builds and returns a new String "BUET CSE", but that returned object was thrown away. To keep it, write s = s.concat(" CSE"); — then it would print BUET CSE.Outer outer = new Outer();
Outer.Inner in = outer.new Inner();
শুধু new Outer.Inner() লিখলে non-static inner class-এ compile হবে না — ওই form শুধু static nested class-এর জন্য। (b) Print হবে BUET। String immutable: concat(" CSE") কখনো s বদলায় না — নতুন String "BUET CSE" বানিয়ে return করে, কিন্তু সেই return করা object-টা আমরা ফেলে দিয়েছি। রাখতে চাইলে লিখতে হতো s = s.concat(" CSE"); — তাহলে print হতো BUET CSE।@FunctionalInterface annotation do? (b) Name two built-in functional interfaces. (c) Define a functional interface Square with method int calc(int x), and implement it with a lambda that returns \(x^2\). Print calc(5).@FunctionalInterface annotation কী কাজ করে? (b) দুইটা built-in functional interface-এর নাম লিখুন। (c) int calc(int x) method-সহ একটা functional interface Square define করুন, আর এমন একটা lambda দিয়ে implement করুন যেটা \(x^2\) return করে। calc(5) print করুন।Show Answerউত্তর দেখুন
@FunctionalInterface is optional but useful: it makes the compiler give an error if the interface ever gets a second abstract method — so the lambda-friendliness is protected. (b) Any two of: Runnable (run()), Comparator (compare()), Function (apply()), Predicate (test()). (c)
@FunctionalInterface
interface Square {
int calc(int x);
}
public class Main {
public static void main(String[] args) {
Square sq = x -> x * x; // lambda implements calc
System.out.println(sq.calc(5)); // 25
}
}
The lambda x -> x * x becomes the body of calc; Java knows this because Square has only that one abstract method.@FunctionalInterface optional কিন্তু কাজের: কেউ পরে দ্বিতীয় abstract method যোগ করলে compiler error দেয় — ফলে lambda-friendliness রক্ষা পায়। (b) যেকোনো দুইটা: Runnable (run()), Comparator (compare()), Function (apply()), Predicate (test())। (c)
@FunctionalInterface
interface Square {
int calc(int x);
}
public class Main {
public static void main(String[] args) {
Square sq = x -> x * x; // lambda implements calc
System.out.println(sq.calc(5)); // 25
}
}
Lambda x -> x * x হয়ে যায় calc-এর body; Java এটা বোঝে কারণ Square-এ ওই একটাই abstract method।button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
count++;
}
});
(b) A list List<String> names is given. WITHOUT writing any loop, produce a List<Integer> of the name lengths, using streams.button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
count++;
}
});
(b) একটা list List<String> names দেওয়া আছে। কোনো loop না লিখে, streams ব্যবহার করে name-গুলোর length-এর একটা List<Integer> বানান।Show Answerউত্তর দেখুন
ActionListener is a functional interface (one abstract method, actionPerformed), so the whole block shrinks to one line:
button.addActionListener(e -> count++);
The lambda's parameter e is the ActionEvent; the body is what the method did. (b) Open a stream, map each name to its length, collect the results:
List<Integer> lengths = names.stream()
.map(String::length) // or: n -> n.length()
.collect(Collectors.toList());
map transforms every element (String → Integer), and collect(Collectors.toList()) gathers them into a new list — the stream does all the looping internally, so no for/while appears in our code.ActionListener একটা functional interface (একটাই abstract method, actionPerformed), তাই পুরো block এক লাইনে নেমে আসে:
button.addActionListener(e -> count++);
Lambda-র parameter e-ই হলো ActionEvent; body-টা method-টা যা করত তাই। (b) একটা stream খুলুন, প্রতিটা name-কে তার length-এ map করুন, result collect করুন:
List<Integer> lengths = names.stream()
.map(String::length) // বা: n -> n.length()
.collect(Collectors.toList());
map প্রতিটা element বদলে দেয় (String → Integer), আর collect(Collectors.toList()) সেগুলো নতুন list-এ জমা করে — loop চালানোর কাজটা stream ভেতরে ভেতরে নিজেই করে, তাই আমাদের code-এ কোনো for/while নেই।