C++における抽象クラス、テンプレート、スマートポインタの実践的応用

抽象基底クラスを定義し、派生クラスで具象化することで多態性を実現する設計パターンは、C++におけるオブジェクト指向の核心です。以下では、出版物管理システムを例に、継承と仮想関数の適切な使い方を示します。

// base_item.hpp
#pragma once
#include <string>

class MediaItem {
protected:
    std::string title_;
public:
    explicit MediaItem(const std::string& t) : title_(t) {}
    virtual ~MediaItem() = default;
    virtual void displayInfo() const = 0;
    virtual double calculateValue() const = 0;
    std::string getTitle() const { return title_; }
};
// novel.cpp
#include "base_item.hpp"
#include <iostream>

class Novel : public MediaItem {
    std::string author_;
    int pageCount_;
public:
    Novel(const std::string& t, const std::string& a, int p)
        : MediaItem(t), author_(a), pageCount_(p) {}

    void displayInfo() const override {
        std::cout << "[小説] タイトル: " << title_ 
                  << ", 著者: " << author_
                  << ", ページ数: " << pageCount_ << '\n';
    }

    double calculateValue() const override {
        return pageCount_ * 0.8;
    }
};

仮想デストラクタの有無がメモリ管理に与える影響を検証します。仮想デストラクタを省略すると、基底クラスポインタ経由でのdelete時に派生クラスのクリーンアップ処理がスキップされ、リソースリークを引き起こす可能性があります。

void demonstratePolymorphism() {
    MediaItem* items[] = {
        new Novel("雪国", "川端康成", 256),
        new AudioBook("銀河鉄道の夜", "宮沢賢治", 180)
    };

    for (auto item : items) {
        item->displayInfo();
        delete item; // 仮想デストラクタにより適切な破棄が保証される
    }
}

テンプレートを活用した汎用コンテナの実装例として、複素数クラスを再設計します。型特性チェックと演算子オーバーロードにより、直感的なインターフェースを提供します。

// complex_number.hpp
template<typename NumType>
class ComplexNumber {
    static_assert(std::is_arithmetic_v<NumType>, "数値型のみ許可");

    NumType realPart_, imagPart_;

public:
    ComplexNumber(NumType r = 0, NumType i = 0) : realPart_(r), imagPart_(i) {}

    ComplexNumber operator+(const ComplexNumber& other) const {
        return {realPart_ + other.realPart_, imagPart_ + other.imagPart_};
    }

    bool operator==(const ComplexNumber& other) const {
        return realPart_ == other.realPart_ && imagPart_ == other.imagPart_;
    }

    friend std::ostream& operator<<(std::ostream& os, const ComplexNumber& c) {
        os << c.realPart_;
        if (c.imagPart_ >= 0) os << " + ";
        else os << " - ";
        os << std::abs(c.imagPart_) << "i";
        return os;
    }
};

スマートポインタによる自動メモリ管理の利点を示すために、ペットシミュレータを改良します。unique_ptrを使用することで、明示的なdelete不要で例外安全なコードが得られます。

void modernMemoryManagement() {
    std::vector<std::unique_ptr<MediaItem>> collection;
    
    collection.emplace_back(std::make_unique<Novel>("舞姫", "森鴎外", 192));
    collection.emplace_back(std::make_unique<AudioBook>("走れメロス", "太宰治", 120));

    for (const auto& item : collection) {
        std::cout << item->getTitle() << " の価値: " 
                  << item->calculateValue() << "円\n";
    }
    // 自動的にメモリ解放 - デストラクタが正しく呼び出される
}

関数オブジェクトとラムダ式を用いた柔軟なソート機構の実装も重要です。比較関数をテンプレート化することで、異なる基準での並び替えが可能になります。

template<typename Container, typename Comparator>
void sortByCriteria(Container& container, Comparator comp) {
    std::sort(container.begin(), container.end(), comp);
}

// 使用例
sortByCriteria(salesData, [](const SaleRecord& a, const SaleRecord& b) {
    return a.getRevenue() > b.getRevenue(); // 売上高降順
});

タグ: C++ 抽象クラス テンプレート スマートポインタ 多態性

7月19日 23:34 投稿