2. C++ Basics: Essential Differences from C
1.2. Theory
C++ 1.Basics: TheoreticalEssential BackgroundDifferences from C
1.2.1 DefinitionBasic ofSyntax OOPDifferences
Object-OrientedC Programmingvs (OOP)C++ Comparison:
| Feature | C | C++ |
|---|---|---|
. |
.cpp |
|
scanf(), printf() |
cin, cout |
|
| Header Files | #include <stdio.h> |
#include <iostream> |
| Namespace | Not used | using namespace std; |
| Comments | /* */ and // |
/* and // ( |
| Boolean Type |
int (0/1) |
bool (true/false) |
| String Type | char[] |
string class |
1.2.2 EncapsulationHello World Comparison
EncapsulationC is one of the four fundamental principles of Object-Oriented Programming (OOP).
It refers to the process of wrapping attributes (data members) and behaviors (methods) inside a class and restricting direct access to the data from outside.
Instead of accessing attributes directly, developers use getterProgram: and setter functions.
This provides better data security, controlled access, and modularity in software design.
class#include Device {
private:<stdio.h>
int batteryLevel; // accessible only via setter/getter
public:
void setBattery(int b) { batteryLevel = b; }
int getBattery(main() {
printf("Hello, World!\n");
return batteryLevel;0;
}
};

Access Specifiers in Encapsulation
C++ provides three main access specifiers that control how members of a class can be accessed:
Public Members declared as public can be accessed from anywhere in the program (inside and outside the class). Commonly used for interfaces, such as getters, setters, or functions that need to be available to other classes.
class Phone {
public:
void call() {
cout << "Calling..." << endl;
}
};
Protected Members declared as protected are only accessible within the class itself and its derived (child) classes. They are not accessible from outside the class. Commonly used in inheritance when you want child classes to access attributes, but still prevent external direct access.
class Gadget {
protected:
int warrantyYears;
};
class Laptop : public Gadget {
public:
void setWarranty(int w) {
warrantyYears = w; // accessible here
}
};
Private Members declared as private are only accessible within the class itself. They are completely hidden from outside access, including derived classes. Commonly used for sensitive data that must be fully encapsulated.
class BankAccount {
private:
double balance;
public:
void deposit(double amount) {
balance += amount;
}
double getBalance() {
return balance;
}
};
1.3 Abstraction
Abstraction allows a programmer to display only the essential features without revealing implementation details.
In C++, this is often done with abstract classes and pure virtual functions.
class Vehicle {
public:
virtual void move() = 0; // pure virtual → must be overridden by derived class
};
1.4 Pure Virtual Function
Normal virtual function → has a default implementation, can be overridden.Pure virtual function → has no implementation and makes a class abstract.
Objects cannot be created from an abstract class; only from its derived classes.Program:
#include <iostream>
using namespace std;
//int Abstract base class
class Device {
public:
// Pure virtual function → no implementation here
virtual void start(main() = 0;
};
// Derived class Smartphone
class Smartphone : public Device {
public:
void start() override {
cout << "SmartphoneHello, is starting with touch screen...World!" << endl;
}
};
// Derived class Laptop
class Laptop : public Device {
public:
void start() override {
cout << "Laptop is starting with power button..." << endl;
}
};
int main() {
Device* d1 = new Smartphone();
Device* d2 = new Laptop();
d1->start(); // Output: Smartphone is starting with touch screen...
d2->start(); // Output: Laptop is starting with power button...
delete d1;
delete d2;
return 0;
}
2.3 1.5Input/Output Relevantin SOLID PrinciplesC++
-
SingleBasic ResponsibilityI/O PrincipleOperations:
#include <iostream>
using namespace std;
int main() {
int age;
string name;
float height;
// Output (SRP)cout Thewith Singleinsertion Responsibilityoperator Principle<<)
cout << "Enter your name: ";
// Input (SRP)cin stateswith thatextraction operator >>)
cin >> name;
cout << "Enter your age: ";
cin >> age;
cout << "Enter your height (m): ";
cin >> height;
// Multiple outputs
cout << "Name: " << name << endl;
cout << "Age: " << age << " years" << endl;
cout << "Height: " << height << " meters" << endl;
return 0;
}
C vs C++ I/O Comparison:
Operation
C
C++
Output integer
printf("%d", x);
cout << x;
Output float
printf("%.2f", x);
cout << fixed << setprecision(2) << x;
Output string
printf("%s", str);
cout << str;
Input integer
scanf("%d", &x);
cin >> x;
Input string
scanf("%s", str);
cin >> str;
Multiple inputs
scanf("%d %d", &a, &b);
cin >> a class>> shouldb;
only
have
one
reason to change, meaning it should focus onReading a singleFull responsibility. This avoids bloated classes that handle too many tasks at once. For example, instead of having one class that manages both appliance details and logging, these should be separated into two different classes.Line:
#include <iostream>
#include <string>
using namespace std;
//int SRP: Appliance handles only appliance-related data
class Appliance {
private:
string name;
public:
Appliance(string n) : name(n) {}
string getName(main() {
return name; }
};
// Separate Logger class (not mixed inside Appliance)
class Logger {
public:
void log(string message) {fullName;
cout << "[LOG]:Enter your full name: ";
getline(cin, fullName); // Reads entire line including spaces
cout << "Hello, " << messagefullName << "!" << endl;
}
};
int main() {
Appliance fridge("Refrigerator");
Logger logger;
logger.log(fridge.getName() + " is running...");
return 0;
}
Important Note :on Incin this SRP example, the Appliance class is responsible only for appliance data, while the Logger class is responsible for logging activities. Each class has one clear responsibility.
Liskov Substitution Principle (LSP)Buffer:
The Liskov Substitution Principle (LSP) states that objects of a derived class should be substitutable for objects of their base class without breaking program behavior. In practice, this means that if a base class reference is used to call a function, it should work correctly with any derived class.
#includeint <iostreamage;
string name;
cin >> using namespace std;age; // BaseUser class
class Appliance {
public:
virtual void start() {
cout <<enters "Starting25" aand genericpresses appliance..." << endl;
}
};Enter
// DerivedBuffer classnow WashingMachine
class WashingMachine : public Appliance {
public:
void start() override {
cout << "Washing machine is startingcontains the washnewline cycle..."character
<< endl;
}
};
// Derived class Refrigerator
class Refrigerator : public Appliance {
public:
void start() override {
cout << "Refrigerator is cooling..." << endl;
}
};
int main() {
Appliance* a1 = new WashingMachine();
Appliance* a2 = new Refrigerator();
a1->start(cin.ignore(); // WorksClear asthe anewline WashingMachinefrom a2->start()buffer
getline(cin, name); // WorksNow ascan aread Refrigeratorfull deleteline a1;
delete a2;
return 0;
}properly
Note : Here, both WashingMachine and Refrigerator can be substituted for Appliance without issues. This shows LSP because the derived classes behave correctly when used in place of the base class.
SingleBasic ResponsibilityI/O PrincipleOperations:
#include <iostream>
using namespace std;
int main() {
int age;
string name;
float height;
// Output (SRP)cout Thewith Singleinsertion Responsibilityoperator Principle<<)
cout << "Enter your name: ";
// Input (SRP)cin stateswith thatextraction operator >>)
cin >> name;
cout << "Enter your age: ";
cin >> age;
cout << "Enter your height (m): ";
cin >> height;
// Multiple outputs
cout << "Name: " << name << endl;
cout << "Age: " << age << " years" << endl;
cout << "Height: " << height << " meters" << endl;
return 0;
}
C vs C++ I/O Comparison:
| Operation | C | C++ |
|---|---|---|
| Output integer | printf("%d", x); |
cout << x; |
| Output float | printf("%.2f", x); |
cout << fixed << setprecision(2) << x; |
| Output string | printf("%s", str); |
cout << str; |
| Input integer | scanf("%d", &x); |
cin >> x; |
| Input string | scanf("%s", str); |
cin >> str; |
| Multiple inputs | scanf("%d %d", &a, &b); |
cin >> a |
Reading a singleFull responsibility. This avoids bloated classes that handle too many tasks at once. For example, instead of having one class that manages both appliance details and logging, these should be separated into two different classes.Line:
#include <iostream>
#include <string>
using namespace std;
//int SRP: Appliance handles only appliance-related data
class Appliance {
private:
string name;
public:
Appliance(string n) : name(n) {}
string getName(main() {
return name; }
};
// Separate Logger class (not mixed inside Appliance)
class Logger {
public:
void log(string message) {fullName;
cout << "[LOG]:Enter your full name: ";
getline(cin, fullName); // Reads entire line including spaces
cout << "Hello, " << messagefullName << "!" << endl;
}
};
int main() {
Appliance fridge("Refrigerator");
Logger logger;
logger.log(fridge.getName() + " is running...");
return 0;
}
Important Note :on Incin this SRP example, the Appliance class is responsible only for appliance data, while the Logger class is responsible for logging activities. Each class has one clear responsibility.
Liskov Substitution Principle (LSP)Buffer:
The Liskov Substitution Principle (LSP) states that objects of a derived class should be substitutable for objects of their base class without breaking program behavior. In practice, this means that if a base class reference is used to call a function, it should work correctly with any derived class.
#includeint <iostreamage;
string name;
cin >> using namespace std;age; // BaseUser class
class Appliance {
public:
virtual void start() {
cout <<enters "Starting25" aand genericpresses appliance..." << endl;
}
};Enter
// DerivedBuffer classnow WashingMachine
class WashingMachine : public Appliance {
public:
void start() override {
cout << "Washing machine is startingcontains the washnewline cycle..."character
<< endl;
}
};
// Derived class Refrigerator
class Refrigerator : public Appliance {
public:
void start() override {
cout << "Refrigerator is cooling..." << endl;
}
};
int main() {
Appliance* a1 = new WashingMachine();
Appliance* a2 = new Refrigerator();
a1->start(cin.ignore(); // WorksClear asthe anewline WashingMachinefrom a2->start()buffer
getline(cin, name); // WorksNow ascan aread Refrigeratorfull deleteline a1;
delete a2;
return 0;
}properly
Note : Here, both WashingMachine and Refrigerator can be substituted for Appliance without issues. This shows LSP because the derived classes behave correctly when used in place of the base class.