Skip to main content

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:

isaprogrammingparadigmthatusestheconceptof Anobjectcombines(attributes) (methods)methodoverriding).
Feature C C++
objectsFile Extension.
c
.cpp
dataInput/Output scanf(), printf()cin, cout
Header Files#include <stdio.h>#include <iostream>
NamespaceNot usedusing namespace std;
Comments/* */ and behavior// /* into a single unit.

The four main pillars of OOP:

  • Encapsulation → Hiding data implementation using restricted access through setter/getter.
  • Abstraction → Hiding implementation details while exposing only relevant interfaces.
  • Inheritance → Derived classes can inherit attributes*/ and methods from a base class.
  • Polymorphism → The ability of objects to take many forms// (e.g.,preferred)
Boolean
Type
int (0/1)bool (true/false)
String Typechar[]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;
}
};

image

Access Specifiers in Encapsulation

C++ provides three main access specifiers that control how members of a class can be accessed:

  1. 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;
    }
};
  1. 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
    }
};
  1. 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:

    onlyhaveone
    OperationCC++
    Output integerprintf("%d", x);cout << x;
    Output floatprintf("%.2f", x);cout << fixed << setprecision(2) << x;
    Output stringprintf("%s", str);cout << str;
    Input integerscanf("%d", &x);cin >> x;
    Input stringscanf("%s", str);cin >> str;
    Multiple inputsscanf("%d %d", &a, &b);cin >> a class>> shouldb;
    reason to change, meaning it should focus on

    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.