【C++】interfaceをC++で実現する


# はじめに

C++にはJavaやC#のような`interface`キーワードが存在しませんが，インターフェイス自体は仮想関数などを通じて実現できます．

私が普段C++を書く中でインターフェイスを使用することが多々あるため，備忘録も兼ねてC++でインターフェイスをどのように実装し利用するかを解説します．

# インターフェイスの実装

### インターフェイスの例 (C#)
たとえば、C#では次のように`interface`キーワードを使ってインターフェイスを定義します。

```C#:IShape.cs
public interface IShape
{
    void Draw();
}

```
これを実装するクラスは、インターフェイスで定義されたメソッド`draw()`の実装を提供する必要があります。

```C#:Circle.cs
public class Circle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Drawing a Circle");
    }
}

public class Square : IShape
{
    public void Draw()
    {
        Console.WriteLine("Drawing a Square");
    }
}

```

### C++の実装

C++には`interface`というキーワードは存在しないので，純粋仮想関数（Pure Virtual Function）を持つ抽象クラスを使用してインターフェイスを実現します．
純粋仮想関数とは，そのクラス内で実装を持たず派生クラスに実装を強制するものです．純粋仮想関数を1つ以上持つクラスは、抽象クラスとして扱われます．

```cpp:i_shape.h
class IShape {
  public:
    virtual ~IShape() = default;   // 仮想デストラクタ
    virtual void draw() const = 0; // 純粋仮想関数
};
```

注意点として，デストラクタを仮想デストラクタにする必要があります．


先ほどと同様にこのインターフェイスを実装するクラスは，すべての純粋仮想関数を実装する必要があります．

```cpp:circle.h
#include <iostream>

#include "i_shape.h"

class Circle final : public IShape {
  public:
    void draw() const override {
        std::cout << "Drawing a Circle" << std::endl;
    }
};

class Square final : public IShape {
  public:
    void draw() const override {
        std::cout << "Drawing a Square" << std::endl;
    }
};
```

# インターフェイスの利用

### インターフェイスの利用 (C#)

C#ではインターフェイスを実装したクラスをインスタンス化し，インターフェイスのメソッドを呼び出すことができます．

```C#:Program.cs
class Program
{
    static void Main()
    {
        IShape circle = new Circle();
        IShape square = new Square();

        circle.Draw();
        square.Draw();
    }
}
```

```C#:Output
Drawing a Circle
Drawing a Square
```

### C++の利用

C++でも同様にインターフェイスを実装したクラスをインスタンス化し，インターフェイスのメソッドを呼び出すことができます．
C++の`new`キーワードはC#ほど使いやすくないため，`std::make_unique`を使用してインスタンスを生成すると簡単です．

```cpp:main.cpp
#include <memory>

#include "circle.h"

int main() {
    const std::unique_ptr<IShape> circle = std::make_unique<Circle>();
    const std::unique_ptr<IShape> square = std::make_unique<Square>();

    circle->draw();
    square->draw();

    return 0;
}
```

```cpp:Output
Drawing a Circle
Drawing a Square
```

# DI (Dependency Injection) の例

C#では簡単にできますが，C++では`std::unique_ptr`を使用する場合，インスタンスの所有権を移動させるために`std::move`を使用する必要があります．
以下に簡単なサンプルを示します．

### DI (Dependency Injection) (C#)

```C#:ShapeRenderer.cs
public class ShapeRenderer
{
    private readonly IShape _shape;

    public ShapeRenderer(IShape shape)
    {
        _shape = shape;
    }

    public void Render()
    {
        _shape.Draw();
    }
}
```

```C#:Program.cs
class Program
{
    static void Main()
    {
        IShape circle = new Circle();
        IShape square = new Square();

        ShapeRenderer renderer1 = new ShapeRenderer(circle);
        ShapeRenderer renderer2 = new ShapeRenderer(square);

        renderer1.Render();
        renderer2.Render();
    }
}
```

```C#:Output
Drawing a Circle
Drawing a Square
```

### DI (Dependency Injection) (C++)

```cpp:shape_renderer.h
#include <memory>

#include "i_shape.h"

class ShapeRenderer final {
  public:
    explicit ShapeRenderer(std::unique_ptr<IShape>&& shape)
        : shape_(std::move(shape)) {}

    void render() const {
        shape_->draw();
    }

  private:
    const std::unique_ptr<IShape> shape_;
};
```

```cpp:main.cpp
#include "circle.h"
#include "shape_renderer.h"

int main() {
    std::unique_ptr<IShape> circle = std::make_unique<Circle>();
    std::unique_ptr<IShape> square = std::make_unique<Square>();

    ShapeRenderer renderer1(std::move(circle));
    ShapeRenderer renderer2(std::move(square));

    renderer1.render();
    renderer2.render();

    return 0;
}
```

```cpp:Output
Drawing a Circle
Drawing a Square
```

# 参考

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/interface

unique_ptrを引数に渡すときの書き方を参考にいたしました．

https://nojima.hatenablog.com/entry/2014/12/10/014131