Dependency Injection in C++: A Complete Guide for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What is Dependency Injection in C++ and Why Do You Need It?

\n\n

Dependency Injection (DI) is one of the key principles of object-oriented programming that allows you to make code more flexible, testable, and maintainable. If you are a beginner in C++ and have heard terms like "loose coupling" or "inversion of control" but don't fully understand them — this article is for you.

\n\n

In simple words, Dependency Injection is a technique where an object receives its dependencies from the outside, rather than creating them itself. Instead of a class creating objects of other classes on its own, these objects are "injected" into it via a constructor, setter, or interface.

\n\n

The Problem: Tight Coupling

\n\n

Imagine you are writing an application for sending notifications. Without DI, your code might look like this:

\n\n
#include <iostream>\n#include <string>\n\nclass EmailService {\npublic:\n    void send(const std::string& message) {\n        std::cout << "Sending email: " << message << std::endl;\n    }\n};\n\nclass Notification {\nprivate:\n    EmailService emailService;  // Hard dependency!\npublic:\n    void notify(const std::string& msg) {\n        emailService.send(msg);\n    }\n};\n\nint main() {\n    Notification notif;\n    notif.notify("Hello, World!");\n    return 0;\n}
\n\n

In this example, the Notification class is tightly coupled to EmailService. If you want to add SMS notifications or logging — you will have to rewrite the Notification class. Such code is difficult to test, extend, and maintain.

\n\n

The Solution: Dependency Injection

\n\n

To solve the problem of tight coupling, we use DI. The main idea is to define an interface (abstract class) for the notification service and pass it into the Notification class from the outside.

\n\n

Step 1: Create an Abstract Interface

\n\n
#include <iostream>\n#include <string>\n#include <memory>\n\n// Abstract interface for all notification services\nclass IMessageService {\npublic:\n    virtual ~IMessageService() = default;\n    virtual void send(const std::string& message) = 0;\n};
\n\n

Step 2: Implement Concrete Services

\n\n
class EmailService : public IMessageService {\npublic:\n    void send(const std::string& message) override {\n        std::cout << "📧 Email: " << message << std::endl;\n    }\n};\n\nclass SMSService : public IMessageService {\npublic:\n    void send(const std::string& message) override {\n        std::cout << "📱 SMS: " << message << std::endl;\n    }\n};
\n\n

Step 3: Inject the Dependency via Constructor

\n\n
class Notification {\nprivate:\n    std::unique_ptr<IMessageService> service;  // Dependency is injected from outside\npublic:\n    // Constructor Injection\n    Notification(std::unique_ptr<IMessageService> svc) \n        : service(std::move(svc)) {}\n    \n    void notify(const std::string& msg) {\n        service->send(msg);\n    }\n};\n\nint main() {\n    // Create a concrete implementation\n    auto emailService = std::make_unique<EmailService>();\n    \n    // Inject it into Notification\n    Notification notif(std::move(emailService));\n    notif.notify("Dependency Injection works!");\n    \n    // Easily switch to SMS\n    auto smsService = std::make_unique<SMSService>();\n    Notification smsNotif(std::move(smsService));\n    smsNotif.notify("Now via SMS!");\n    \n    return 0;\n}
\n\n

Three Main Ways to Inject Dependencies in C++

\n\n

1. Constructor Injection

\n

The most common and recommended method. Dependencies are passed when creating the object and remain unchanged throughout its lifetime.

\n\n
<                

Blogs

Book Recommendations