[C++] OOPs - Templates and File Handling
Suppose you and I sit together, and I tell you —
"Hey, let’s build a magical LEGO machine. It should be able to assemble a car today, a spaceship tomorrow, and maybe even a castle the day after!"
Wouldn’t that be super cool?
One machine — doing different tasks, depending on what blocks you feed it.
Now pause and think — in C++ programming, don't we often need something similar?
A way to write a logic once, but apply it to different types without rewriting everything again?
That’s Templates!
And when it comes to storing all those finished LEGO models somewhere safe for later use, we’d need a storage room — that's like File Handling!
In this article, we’re going to deeply understand two huge parts of C++:
- Templates (Function Templates + Class Templates)
- File Handling (Opening, Closing, Writing, Reading, File Pointers)
And trust me, once we truly get them, everything in programming will start to feel easier and more powerful.
Why Do We Even Need Templates?
Let’s start with a simple question:
Suppose we need a function to find the bigger of two numbers. Easy, right? Here's a simple function:
// Function to find maximum of two integers
int findMax(int a, int b) {
return (a > b) ? a : b;
}
Works fine for integers!
But what if I want to find the max of two floats? Two doubles? Two strings?
Would I write this again for every type?
- float findMax(float a, float b);
- double findMax(double a, double b);
- string findMax(string a, string b);
Wait... isn’t this repetitive?
Isn't there a smarter way?
Just like our LEGO machine, can’t we make a general findMax machine — and just feed it whatever types we want?
That's where Templates come to the rescue!
What is a Function Template?
Let’s slowly build the intuition.
Imagine I could write:
"Hey C++, create a function findMax, but don’t commit to any one data type yet. I'll tell you later when I use it."
This is Function Template.
Here’s the code:
#include <iostream>
using namespace std;
// Template for a function that finds maximum
template <typename T>
T findMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << findMax(5, 10) << endl; // Works with int
cout << findMax(5.7, 3.2) << endl; // Works with double
cout << findMax('a', 'z') << endl; // Works with char
return 0;
}
What Did We Do Here?
- template<typename T>:
We are telling C++:
"Hey, T is just a placeholder. When someone calls the function, you replace T with whatever type they use!" - T findMax(T a, T b):
A single function that works for int, float, char, string, anything comparable! - Magic Moment:
When we call findMax(5.7, 3.2), C++ automatically realizes T is a double!
Multiple Types? Why Not!
Suppose you want to compare two different types — like an int and a double?
We can modify it:
// Template for two different types
template <typename T1, typename T2>
auto findMax(T1 a, T2 b) -> decltype((a > b) ? a : b) {
return (a > b) ? a : b;
}
Huh? What's happening here?
- T1 and T2 can be different.
- decltype(...) tells C++: "Whatever the result of (a > b ? a : b) is, use that as the return type."
Isn’t it beautiful?
Like a smart LEGO machine that adjusts itself based on the blocks you give!
Quick Recap:
Topic | Meaning |
---|---|
template<typename T> | T is a placeholder for a data type |
Single Function Template | One generic function for all types |
Multiple Type Template | Works even when types differ |
decltype | Let C++ figure out the type |
Why Class Templates?
Now, let's push the idea.
Suppose we are building a Box class to store stuff.
First, we need to store int stuff.
Then string stuff.
Maybe tomorrow float stuff.
Should we write a Box class again and again for each type?
Of course not!
Let’s make a generic Box template.
Class Template Example
#include <iostream>
using namespace std;
// Template for a Box class
template <class T>
class Box {
private:
T item;
public:
// Constructor
Box(T value) {
item = value;
}
// Function to display the item
void show() {
cout << "Item: " << item << endl;
}
};
int main() {
Box<int> intBox(100);
intBox.show(); // Item: 100
Box<string> stringBox("Hello Templates!");
stringBox.show(); // Item: Hello Templates!
return 0;
}
What’s Happening?
- template<class T>:
The Box doesn't commit to a data type yet. - Box<int>:
T becomes int. - Box<string>:
T becomes string.
Same class — different data types!
Just like our LEGO machine could assemble cars or spaceships — depending on parts we gave it.
Important: Template Gotchas
Templates are powerful but tricky. Some things to watch for:
Gotcha | Why It Happens |
---|---|
Template code must be available at compile-time | Because C++ needs to generate the correct version |
Can cause huge binaries if not careful | Too many template instantiations |
Templates can't be separated into .cpp and .h easily | Because of the way they work |
Debugging templates can be hard | Compiler errors are long and messy |
Part 2: File Handling in C++
Why Do We Need File Handling?
Imagine if every time you closed your program, all your hard work disappeared.
All student records, all shopping lists, all bank transactions — gone!
Poof!
That's why we store data in files — so it survives even after the program exits.
Just like saving your LEGO models in a showcase!
Basics: Opening and Closing Files
First things first: How do we even talk to a file?
In C++, we use three main classes (from <fstream> library):
Class |
Purpose |
ifstream |
Input File Stream (reading) |
ofstream |
Output File Stream (writing) |
fstream |
Both Input and Output |
Opening a File
Let's open a file for writing:
#include <fstream>
using namespace std;
int main() {
ofstream outFile("example.txt"); // Open file for writing
if (!outFile) {
cout << "Error opening file!" << endl;
return 1;
}
outFile << "Hello, World!" << endl; // Write to file
outFile.close(); // Always close!
return 0;
}
Explaining Step-by-Step
- ofstream outFile("example.txt");
Open (or create) a file called example.txt for writing. - if (!outFile)
Always check if file opened successfully. - outFile << "Hello, World!";
Just like cout, but going into file! - outFile.close();
Very important. Always close when done.
Similarly, ifstream opens for reading.
fstream can be used for both.
Modes of Opening Files
You can open files in different modes:
Mode |
Meaning |
ios::in |
Read only |
ios::out |
Write only |
ios::app |
Append at end |
ios::ate |
Go to end on open |
ios::binary |
Binary mode |
ofstream file("data.txt", ios::app);
Means:
"Open data.txt, and if it exists, add at the end instead of overwriting."
Writing Data to Files
Writing is easy — just like printing.
ofstream outFile("info.txt");
outFile << "Name: John Doe" << endl;
outFile << "Age: 25" << endl;
outFile.close();
Reading Data from Files
Similarly, reading is easy too!
ifstream inFile("info.txt");
string line;
while (getline(inFile, line)) {
cout << line << endl;
}
inFile.close();
- getline() reads one line at a time.
- Stops when end of file (EOF) reached.
A Full Example: Read + Write
Let's build a small project — saving and showing student info!
#include <fstream>
#include <iostream>
using namespace std;
int main() {
ofstream studentFile("students.txt");
string name;
int age;
cout << "Enter student's name: ";
getline(cin, name);
cout << "Enter student's age: ";
cin >> age;
studentFile << name << endl;
studentFile << age << endl;
studentFile.close();
// Now read it back
ifstream readFile("students.txt");
string storedName;
int storedAge;
getline(readFile, storedName);
readFile >> storedAge;
cout << "Student Name: " << storedName << endl;
cout << "Student Age: " << storedAge << endl;
readFile.close();
return 0;
}
Important Tip:
Always check:
if (!file)
cout << "File failed to open!";
Handling file errors is critical for big applications.
Advanced: File Position Pointers
Now, what if we want to jump to a specific place inside a file?
That’s where file position pointers come in!
Pointer |
What it controls |
get pointer |
Where to read next |
put pointer |
Where to write next |
Functions:
Function |
Meaning |
seekg() |
move get pointer |
seekp() |
move put pointer |
tellg() |
get get pointer |
tellp() |
get put pointer |
Example:
ifstream file("example.txt");
file.seekg(5); // Move to 5th byte
char c;
file.get(c); // Read character at position 5
cout << c << endl;
file.close();
Isn't that awesome?
You can literally jump around inside a file!
Common Interview Questions (and Hints)
Question | Hint |
---|---|
What is a template? | Generic programming |
What is a function template vs class template? | Function vs Class genericity |
Why templates overloading needed? | Different types, same logic |
What are file streams? | Communication between program and file |
Difference between ifstream and fstream? | Read vs read+write |
What is EOF? | End of File |
What is seekg and seekp? | Move file pointers |
Why should you always close files? | Save data and free resources |
Can template functions be overloaded? | Yes, but carefully |
When would you use template specialization? | Custom behavior for specific types |
Final Summary
Templates make your code reusable, flexible, and type-safe.
File Handling helps your program store data persistently.
Together, they help you build smart, powerful, scalable applications.
Think of Templates as your LEGO builders — smart, flexible, reusable.
Think of File Handling as your storage room — organized, permanent, and reliable.
Congratulations!
You now understand two of the most crucial superpowers in C++.