# Object-Oriented Programming with C++

> Detailed C++ notes on classes, object lifetime, references, resource management, overloading, inheritance, polymorphism, streams and the standard library.

- Author: Muhammet Ali Köker
- Language: en
- Canonical: https://alikoker.com.tr/en/object-oriented-programming-with-cpp
- Translation: https://alikoker.com.tr/cpp-ile-nesne-yonelimli-programlama
- Published: 2014-10-12T19:05:00+03:00
- Modified: 2026-07-14T18:25:00+03:00
- Verified: 2026-08-08T15:00:00+03:00
- Type: article

These C++ notes are the object-oriented programming and resource-management part of the C/C++ material I kept during the same course period. Later revisions add concepts that became established after C++11, but the original progression through classes, object lifetime, inheritance, polymorphism and the standard library remains intact.

## Unit 1: Moving from C to C++

### Relationship between C and C++

C++ grew historically from C and retains many low-level facilities, but it is a separate language with its own object model, type rules, standard library and design idioms. Valid C is not automatically good C++, and some C constructs do not have identical semantics in C++.

A minimal program is:

```cpp
#include <iostream>

int main()
{
    std::cout << "hello\n";
    return 0;
}
```

The standard library is organized in namespaces, primarily `std`. A global `using namespace std;` may shorten teaching examples, but production headers should avoid exporting such broad namespace directives to their users.

### Initialization

C++ supports several initialization forms:

```cpp
int a = 10;
int b(10);
int c{10};
```

Brace initialization is useful because it rejects many narrowing conversions:

```cpp
int x{3.5}; // ill-formed
```

The exact choice should reflect the desired constructor and conversion semantics rather than a stylistic rule applied blindly.

### `auto`

`auto` asks the compiler to deduce a variable's type from its initializer:

```cpp
auto count = 10;
auto ratio = 0.5;
```

It reduces redundant type spelling, especially with iterators and template-heavy types. It does not make C++ dynamically typed; the deduced type is still a compile-time type.

### `const` and `constexpr`

`const` prevents modification through that name or access path where the type system applies it. `constexpr` expresses that an entity can participate in constant evaluation when its requirements are satisfied.

```cpp
constexpr int buffer_size = 4096;
```

The two concepts overlap but are not identical: `const` is primarily about mutability, while `constexpr` is about constant-expression semantics.

### `nullptr`

C++11 introduced `nullptr` as a dedicated null pointer literal:

```cpp
int *p = nullptr;
```

It avoids overload ambiguities caused by integer literal `0` or macro-style `NULL` values.

### Streams, strings and vectors

C++ standard I/O uses streams:

```cpp
std::cout << value << '\n';
std::cin >> value;
```

`std::string` owns a dynamically sized character sequence and is generally preferable to manual C-string management for ordinary text processing:

```cpp
std::string name = "Ali";
```

`std::vector<T>` is the standard contiguous dynamic sequence container:

```cpp
std::vector<int> values{1, 2, 3};
values.push_back(4);
```

It manages storage automatically and keeps ownership with the container.

## Unit 2: Object-Oriented Programming

### Objects and classes

A class defines state and operations that maintain the invariants of objects of that type:

```cpp
class Counter {
private:
    int value_;

public:
    explicit Counter(int value) : value_(value) {}
    void increment() { ++value_; }
    int value() const { return value_; }
};
```

Object-oriented design is not merely a mechanism for grouping fields and functions. The useful abstraction is one in which operations preserve a coherent model and hide representation details that callers should not depend on.

### Abstraction and encapsulation

Abstraction exposes the properties relevant to users of a type. Encapsulation controls access to representation and implementation detail. C++ provides `public`, `private` and `protected` access control.

A `struct` and a `class` have almost the same language capabilities; their main default differences are access and inheritance visibility. `struct` defaults to public members, while `class` defaults to private members.

### Constructors

A constructor establishes a valid object state:

```cpp
class Point {
    int x_;
    int y_;
public:
    Point(int x, int y) : x_(x), y_(y) {}
};
```

Members are initialized in declaration order, not in the textual order of the initializer list. This matters when one member depends on another.

A default constructor can be compiler-generated or explicitly requested:

```cpp
Point() = default;
```

A single-argument constructor can unintentionally define an implicit conversion. `explicit` suppresses such conversion where it is not part of the intended interface:

```cpp
explicit Size(std::size_t value);
```

### Destructors and RAII

A destructor runs when an object's lifetime ends:

```cpp
~Resource();
```

C++'s central resource-management idiom is RAII: acquire a resource in object construction and release it in destruction. This associates file handles, locks, memory and other resources with lexical object lifetime, making cleanup work across normal return paths and exception unwinding.

### Copying and assignment

Copy construction creates a new object from an existing one. Copy assignment replaces the state of an already existing object. For resource-owning types, these operations must have well-defined ownership semantics.

If a type can be represented safely by standard value members such as `std::string`, `std::vector` and smart pointers, the **Rule of Zero** is usually preferable: let those members implement ownership so the user-defined class does not need custom copy/move/destructor logic.

### Move semantics

C++11 move semantics allow resources to be transferred from an expiring object rather than copied:

```cpp
T(T&& other);
T& operator=(T&& other);
```

A moved-from object must remain valid according to the guarantees of its type, although its value may be unspecified. Move operations are performance and ownership tools, not permission to access destroyed state.

## Unit 3: References, Object Pointers and Memory Management

### References

A reference is an alias bound to an object:

```cpp
int value = 10;
int& ref = value;
```

References are useful for mandatory non-owning access, while pointers naturally express nullable or reseatable relationships. The semantic distinction is more important than the syntax.

### Passing by value and by reference

Passing a small value type by value is often simplest:

```cpp
int square(int x);
```

A read-only large object can be passed by `const` reference:

```cpp
void process(const std::string& text);
```

A non-const reference can make mutation explicit:

```cpp
void normalize(Vector& v);
```

For APIs, the parameter form should communicate ownership and mutation expectations.

### `this`

Inside a non-static member function, `this` is a pointer to the current object. It is useful when distinguishing members from parameters or returning the current object:

```cpp
return *this;
```

### `new` and `delete`

Raw dynamic allocation exists:

```cpp
T* p = new T;
delete p;

T* a = new T[n];
delete[] a;
```

but direct ownership with raw `new`/`delete` is usually avoidable in modern C++. Containers and RAII wrappers make ownership clearer and exception-safe.

### Smart pointers

`std::unique_ptr<T>` represents exclusive ownership:

```cpp
auto p = std::make_unique<Object>();
```

It is movable but not copyable.

`std::shared_ptr<T>` represents shared ownership through reference counting. It should be used only when ownership is genuinely shared; otherwise it obscures lifetime and adds synchronization/reference-counting overhead. Cycles involving `shared_ptr` require `std::weak_ptr` or a different ownership design.

### Arrays of objects

Prefer containers when the number of objects is dynamic:

```cpp
std::vector<Object> objects;
```

This preserves deterministic destruction and avoids manual array allocation.

## Unit 4: Function and Operator Overloading

### Function overloading

Functions can share a name when their parameter lists distinguish them:

```cpp
void print(int value);
void print(double value);
```

Overload resolution uses compile-time type information and conversion ranking. Return type alone cannot distinguish overloads.

### Default arguments

```cpp
void connect(int timeout_ms = 1000);
```

Default arguments are substituted at the call site according to declarations visible there. Changing a default can therefore affect callers differently from changing an implementation body.

### Constructor overloading and delegating constructors

A class can provide multiple construction forms. C++11 also allows one constructor to delegate to another:

```cpp
Widget() : Widget(0) {}
```

Delegation reduces duplicated initialization logic.

### Operator overloading

User-defined types can overload many operators:

```cpp
Vector operator+(const Vector& a, const Vector& b);
```

An overloaded operator should preserve the ordinary semantic expectation of that operator. Surprising side effects make generic code difficult to reason about.

Comparison operators should define a coherent relation. C++20's three-way comparison can synthesize related comparisons for appropriate types, but older explicit operators remain valid and common.

Prefix and postfix increment are distinct signatures; postfix conventionally takes an unused `int` parameter:

```cpp
Counter& operator++();
Counter operator++(int);
```

## Unit 5: Inheritance and Polymorphism

### Inheritance

Inheritance defines a derived class in terms of a base-class relationship:

```cpp
class Derived : public Base {
    ...
};
```

Public inheritance should model an substitutable "is-a" relation, not simply code reuse. Composition is often more appropriate when one object merely contains or uses another.

Protected and private inheritance change accessibility and conversion relationships and are less commonly appropriate for domain modeling.

### Construction and destruction order

Base subobjects are constructed before derived-class members and the derived constructor body. Destruction occurs in the reverse order. Virtual bases have additional ordering rules defined by the language.

### Multiple inheritance and the diamond

C++ supports multiple direct base classes. A diamond can introduce duplicated base subobjects:

```text
    A
   / \
  B   C
   \ /
    D
```

Virtual inheritance can make `B` and `C` share one `A` subobject when that is the intended model. Multiple inheritance is powerful but increases layout and lifecycle complexity.

### Virtual functions and dynamic dispatch

A virtual function enables runtime dispatch through a base reference or pointer:

```cpp
class Base {
public:
    virtual void run();
};
```

A derived override should use `override` so mismatches are diagnosed:

```cpp
void run() override;
```

A polymorphic base intended for deletion through a base pointer normally needs a virtual destructor:

```cpp
virtual ~Base() = default;
```

A pure virtual function defines an abstract interface:

```cpp
virtual void run() = 0;
```

Dynamic polymorphism uses runtime dispatch. Templates can provide **static polymorphism**, where behavior is selected at compile time without a virtual call. The two mechanisms solve different design problems.

## Unit 6: C++ I/O and Files

### Streams

C++ models I/O as streams. `std::cout`, `std::cerr` and `std::cin` are standard stream objects. Stream state records whether formatted extraction or I/O has failed.

```cpp
int value;
if (std::cin >> value) {
    ...
}
```

Checking stream state is preferable to assuming input succeeded.

### Formatting

The `<iomanip>` facilities can control width, precision, base and other formatting properties. Some manipulators modify persistent stream state, so reusable code should not assume formatting is unchanged by previous operations.

### File streams

```cpp
std::ofstream out("data.txt");
std::ifstream in("data.txt");
```

The stream object closes its file during destruction, demonstrating RAII. Open state and I/O errors should still be tested.

`std::getline` reads a complete line into `std::string`:

```cpp
std::string line;
while (std::getline(in, line)) {
    ...
}
```

### Binary and random access

Binary mode is requested with `std::ios::binary`. `read` and `write` transfer byte sequences. As with C, writing the raw memory image of an arbitrary object does not define a portable serialization format.

`seekg`/`seekp` and `tellg`/`tellp` support positioning where the stream permits it.

## Unit 7: The C++ Standard Library

### STL model

The Standard Template Library style separates:

- containers that own or organize data,
- iterators that identify positions/ranges,
- algorithms that operate on ranges,
- function objects and callable values that customize operations.

This separation allows one algorithm to work with many container types when their iterator requirements are satisfied.

### Sequence containers

`std::vector` provides contiguous dynamic storage and amortized constant-time insertion at the end. Reallocation can invalidate pointers, references and iterators into the old storage.

`std::array<T, N>` is a fixed-size array wrapper with ordinary container semantics.

`std::list` is a doubly linked list. It provides stable node addresses and constant-time insertion/erasure at known positions but has poor locality and no constant-time random access.

`std::deque` supports efficient insertion/removal at both ends and random access, without requiring one contiguous allocation.

### Container adaptors

`std::stack`, `std::queue` and `std::priority_queue` expose restricted interfaces over underlying containers. `priority_queue` provides access to the highest-priority element according to its comparator rather than sorted iteration over all elements.

### Associative and unordered containers

Ordered associative containers such as `std::map` and `std::set` maintain key order and usually provide logarithmic search/insert/erase complexity.

Unordered containers such as `std::unordered_map` use hashing and provide average constant-time lookup under normal hash-distribution assumptions, with different worst-case behavior and iteration semantics.

### Iterators

Iterators generalize traversal. Categories encode supported operations, from single-pass input iteration through random-access and contiguous iteration. An algorithm's complexity and valid operations depend on the iterator category it requires.

### Algorithms

Common algorithms include:

```text
find
sort
count
search
transform
```

For example:

```cpp
std::sort(values.begin(), values.end());
```

requires a random-access range. Algorithms usually do not own the elements; they operate through iterators.

### Lambdas

A lambda defines an unnamed callable:

```cpp
auto pred = [](int x) { return x > 0; };
```

Its capture list determines which surrounding objects or values become part of the closure. Capturing by reference requires lifetime discipline.

### Ranges

C++20 ranges add range-aware algorithms, views and pipelines. They reduce explicit iterator pairing and support lazy composition, but views frequently reference other objects and therefore introduce lifetime considerations of their own.

## Unit 8: Modern Resource and Type Safety

### Raw pointers do not imply ownership

A raw pointer is best treated as an address/non-owning access mechanism unless an API explicitly documents ownership transfer. Ownership should preferably be visible in values, containers or smart-pointer types.

### `std::span`

`std::span<T>` represents a non-owning contiguous sequence with a pointer and extent:

```cpp
void process(std::span<const int> values);
```

It makes length part of the view and is safer than a naked pointer without a corresponding count. It does not extend the lifetime of the referenced storage.

### `std::string_view`

`std::string_view` is a non-owning view of character data. It avoids allocation and copying for read-only string slices, but it can dangle if the original string/storage is destroyed or reallocated.

### Exceptions

Exceptions transfer control from a failure point to a matching handler while unwinding automatic objects:

```cpp
try {
    ...
} catch (const std::exception& e) {
    ...
}
```

RAII is what makes this manageable: resources stored in automatic objects are released as destructors run during unwinding.

### `noexcept`

`noexcept` states that a function is not expected to let exceptions escape. Violating an unconditional `noexcept` results in termination. It is therefore a semantic contract, not a performance decoration.

Move operations that are `noexcept` can also enable stronger behavior in standard containers during reallocation.

### Undefined behavior

C++ contains operations whose behavior is not defined by the language, including many out-of-bounds accesses, use-after-lifetime errors, invalid shifts, certain signed overflows and data races. Optimization assumes that a well-formed executing program does not perform undefined behavior, so the result can be much more severe than a local wrong value.

### Warnings and sanitizers

Compiler warnings should be treated as engineering feedback, not cosmetic output. High warning levels, static analysis and sanitizers such as AddressSanitizer and UndefinedBehaviorSanitizer can expose defects that ordinary tests may not make visible.

### Safe default assumptions

Useful defaults for both C and C++ include:

- initialize objects before reading them,
- make ownership explicit,
- preserve bounds with data,
- avoid unchecked arithmetic when sizes come from external input,
- do not rely on unspecified object representation,
- prefer standard-library resource owners to manual allocation,
- keep interfaces const-correct,
- treat compiler diagnostics and runtime instrumentation as part of verification.

## Fundamental Differences Between C and C++

C and C++ share syntax and low-level capabilities, but they encourage different abstractions. C commonly models state through structures, functions and explicit ownership conventions. C++ adds deterministic object lifetime, constructors/destructors, references, overloading, templates, exceptions, generic containers and compile-time abstraction mechanisms.

A C program converted mechanically to C++ does not automatically become idiomatic C++. Conversely, C++ abstractions should not hide performance or ownership properties that are important to a systems-level design. The useful common principle is to make lifetime, representation, control flow and error behavior explicit enough that the code remains predictable.

## Cite This Work

Köker, M. A. (2014). Object-Oriented Programming with C++. alikoker.com.tr. https://alikoker.com.tr/en/object-oriented-programming-with-cpp

- BibTeX: https://alikoker.com.tr/en/object-oriented-programming-with-cpp.bib
- RIS: https://alikoker.com.tr/en/object-oriented-programming-with-cpp.ris
- CSL-JSON: https://alikoker.com.tr/en/object-oriented-programming-with-cpp.csl.json
