Skip to main content

Object oriented programming

[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:

TopicMeaning
template<typename T>T is a placeholder for a data type
Single Function TemplateOne generic function for all types
Multiple Type TemplateWorks even when types differ
decltypeLet 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:

GotchaWhy It Happens
Template code must be available at compile-timeBecause C++ needs to generate the correct version
Can cause huge binaries if not carefulToo many template instantiations
Templates can't be separated into .cpp and .h easilyBecause of the way they work
Debugging templates can be hardCompiler 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)

QuestionHint
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++.