抽象基底クラスを定義し、派生クラスで具象化することで多態性を実現する設計パターンは、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(); // 売上高降順
});