はじめに
C++にはJavaやC#のようなinterface
キーワードが存在しませんが,インターフェイス自体は仮想関数などを通じて実現できます.
私が普段C++を書く中でインターフェイスを使用することが多々あるため,備忘録も兼ねてC++でインターフェイスをどのように実装し利用するかを解説します.
インターフェイスの実装
インターフェイスの例 (C#)
たとえば、C#では次のようにinterface
キーワードを使ってインターフェイスを定義します。
public interface IShape
{
void Draw();
}
これを実装するクラスは、インターフェイスで定義されたメソッドdraw()
の実装を提供する必要があります。
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つ以上持つクラスは、抽象クラスとして扱われます.
class IShape {
public:
virtual ~IShape() = default; // 仮想デストラクタ
virtual void draw() const = 0; // 純粋仮想関数
};
注意点として,デストラクタを仮想デストラクタにする必要があります.
先ほどと同様にこのインターフェイスを実装するクラスは,すべての純粋仮想関数を実装する必要があります.
#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#ではインターフェイスを実装したクラスをインスタンス化し,インターフェイスのメソッドを呼び出すことができます.
class Program
{
static void Main()
{
IShape circle = new Circle();
IShape square = new Square();
circle.Draw();
square.Draw();
}
}
Drawing a Circle
Drawing a Square
C++の利用
C++でも同様にインターフェイスを実装したクラスをインスタンス化し,インターフェイスのメソッドを呼び出すことができます.
C++のnew
キーワードはC#ほど使いやすくないため,std::make_unique
を使用してインスタンスを生成すると簡単です.
#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;
}
Drawing a Circle
Drawing a Square
DI (Dependency Injection) の例
C#では簡単にできますが,C++ではstd::unique_ptr
を使用する場合,インスタンスの所有権を移動させるためにstd::move
を使用する必要があります.
以下に簡単なサンプルを示します.
DI (Dependency Injection) (C#)
public class ShapeRenderer
{
private readonly IShape _shape;
public ShapeRenderer(IShape shape)
{
_shape = shape;
}
public void Render()
{
_shape.Draw();
}
}
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();
}
}
Drawing a Circle
Drawing a Square
DI (Dependency Injection) (C++)
#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_;
};
#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;
}
Drawing a Circle
Drawing a Square
参考
unique_ptrを引数に渡すときの書き方を参考にいたしました.