Skip to content

Last updated:2026-10-12

C++ Dependency Injection

Recently a member of the Knowledge Planet asked me to cover dependency injection, so I wrote this article. Starting from a practical example, it explains the principles and the implementation; the complete code for the lazy version is available in the Knowledge Planet. Let's get to it.

Outline:

  • Direct dependency
  • Interface dependency injection
  • Template dependency injection

Dependency injection is a commonly used design pattern; it usually adopts an interface/template approach to improve the testability and maintainability of code. Let's look at an example.

Suppose there is a user service class UserService that needs to communicate with a remote server through an HTTP client. In this situation we might write the following three implementations.

1. Direct Dependency

UserService directly depends on HttpClient internally, for example:

class HttpClient {
 public:
  std::string Get(const std::string& url);
  void Post(const std::string& url, const std::string& data);
};

class UserService {
 private:
  HttpClient m_httpClient;
};

UserService would usually have some methods that operate on m_httpClient. This approach is simple and crude, but UserService is coupled to the concrete HttpClient, which makes it hard to replace the dependency and hard to unit test.

This leads to the next two approaches.

2. Interface Dependency Injection

Our goal is to make testing convenient — to test without creating a concrete class, by letting the user mock one out. That suggests interfaces: pull HttpClient out into one, giving us:

struct IHttpClient {
  virtual std::string Get(const std::string& url) = 0;
  virtual void Post(const std::string& url, const std::string& data) = 0;
  virtual ~IHttpClient() = default;
};
class UserService {
 private:
  std::unique_ptr<IHttpClient> m_httpClient;
};

Then at test time we can mock out a client to test with, for example:

class MockHttpClient : public IHttpClient {
public:
  MOCK_METHOD(std::string, Get, (const std::string& url), (override));
  MOCK_METHOD(void, Post, (const std::string& url, const std::string& data), (override));
};

auto mockHttpClient = std::make_unique<MockHttpClient>();
UserService userService(std::move(mockHttpClient));

After this transformation, we achieve:

  • Hiding implementation details in the .cc file
  • Working against an abstract interface
  • Injecting dependencies during object construction

3. Template Dependency Injection

Template dependency injection uses C++ template techniques to pass the dependency as a template parameter to UserService. When the template is instantiated, the concrete HTTP client implementation is specified. This approach resolves dependencies at compile time, requires no virtual function calls, and improves performance. It also lets the UserService class integrate flexibly with different HTTP client implementations and makes unit testing easy.

template <typename THttpClient>
class UserService {
 private:
  THttpClient m_httpClient;
};

This case is easy to test, for example:

UserService<MockHttpClient> userService;

This approach has the following benefits:

  • Dependencies are injected during template instantiation
  • No virtual calls (note that THttpClient is not an interface, so calls can be resolved at compile time)

That is everything for this section; the complete code is in the Knowledge Planet — subscribe to download it!

Comments