嵌入式现代C++教程——模板与继承:CRTP与静态多态¶
引言:当模板遇见继承¶
模板和继承是C++两大核心特性,但它们通常被视为彼此独立的工具——模板用于泛型编程,继承用于面向对象设计。然而,当这两者结合时,会产生一些强大而优雅的模式。
其中最著名的就是奇异递归模板模式(Curiously Recurring Template Pattern,CRTP)。这个名字听起来很奇怪,但它的应用场景非常广泛:从单例模式到对象计数,从多态复制到接口注入。
本章我们将深入探讨CRTP的原理与应用,对比静态多态与动态多态的优劣,并学习如何使用Mixin模式为类添加功能。
CRTP:奇异递归模板模式¶
什么是CRTP?¶
CRTP是一种C++惯用法,其核心思想是:派生类将自己作为模板参数传递给基类。
template<typename Derived>
class Base {
public:
void interface() {
// 基类通过转型调用派生类的实现
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() {
// 派生类的具体实现
}
};
这个模式看起来"奇异"是因为:
- 派生类继承自一个以自己为模板参数的基类
- 基类通过
static_cast<Derived*>(this)访问派生类的成员
为什么需要CRTP?¶
CRTP解决了三个核心问题:
- 静态多态:在不使用虚函数的情况下实现多态行为
- 代码复用:在基类中实现通用逻辑,调用派生类的具体实现
- 编译期类型检查:确保派生类实现了所需的接口
让我们通过一个嵌入式场景来理解——设备驱动框架:
// 设备基类(CRTP)
template<typename Derived>
class DeviceBase {
public:
// 通用初始化流程
void initialize() {
// 1. 硬件复位
static_cast<Derived*>(this)->reset_hardware();
// 2. 配置寄存器
static_cast<Derived*>(this)->configure_registers();
// 3. 校准
static_cast<Derived*>(this)->calibrate();
// 4. 通用后处理
static_cast<Derived*>(this)->set_initialized();
}
// 通用读取流程
auto read() {
static_cast<Derived*>(this)->start_conversion();
while (!static_cast<Derived*>(this)->is_ready()) {
// 等待转换完成
}
return static_cast<Derived*>(this)->read_value();
}
};
// ADC设备实现
class ADCDevice : public DeviceBase<ADCDevice> {
public:
void reset_hardware() {
// ADC特定的复位逻辑
}
void configure_registers() {
// ADC特定的配置
}
void calibrate() {
// ADC特定的校准
}
void set_initialized() {
// 标记初始化完成
}
void start_conversion() {
// 启动ADC转换
}
bool is_ready() {
// 检查转换是否完成
return true;
}
uint16_t read_value() {
// 读取ADC值
return 0;
}
};
// 使用
ADCDevice adc;
adc.initialize();
uint16_t value = adc.read();
关键点:
- 所有设备共享相同的初始化和读取流程
- 每个设备提供自己的具体实现
- 没有虚函数调用,所有调用都可以内联
- 编译期保证类型安全
CRTP的本质:静态多态¶
CRTP实现的是静态多态(编译期多态),与虚函数实现的动态多态(运行时多态)形成对比:
| 特性 | 动态多态(虚函数) | 静态多态(CRTP) |
|---|---|---|
| 绑定时机 | 运行时 | 编译期 |
| 性能开销 | 虚表查找 + 间接调用 | 零开销(可内联) |
| 内存开销 | 每个对象一个vptr | 无额外内存 |
| 类型检查 | 运行时(通过虚表) | 编译期 |
| 代码大小 | 较小(一份函数实现) | 较大(每个类型一份) |
| 二进制兼容性 | 稳定(ABI兼容) | 不稳定(模板实例化) |
| 可扩展性 | 运行时可添加新类型 | 编译期确定 |
CRTP的工作原理¶
类型转换详解¶
CRTP的核心在于static_cast<Derived*>(this):
template<typename Derived>
class Base {
public:
void method() {
Derived* d = static_cast<Derived*>(this);
d->impl(); // 调用派生类方法
}
};
为什么这样是安全的?
当Derived继承自Base<Derived>时:
Base<Derived>的this指针实际上指向Derived对象static_cast不会改变指针值,只是改变编译器的类型理解- 这类似于
void*到具体类型的转换,但更安全
布局保证:
class Derived : public Base<Derived> {
int data;
};
// 内存布局:
// [Base部分] [Derived部分]
// ↑ ↑
// this 派生类数据
编译期类型检查¶
CRTP在编译期检查派生类是否实现了所需接口:
template<typename Derived>
class Base {
public:
void interface() {
// 如果Derived没有实现implementation(),编译失败
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
// 未实现implementation()
};
// 编译错误:'class Derived' has no member named 'implementation'
这种检查发生在实例化时,而非模板定义时。
完整示例:多态复制¶
CRTP的经典应用是实现多态的clone()方法:
template<typename Derived>
class Cloneable {
public:
// 克隆接口,返回正确的派生类类型
[[nodiscard]] Derived* clone() const {
return new Derived(static_cast<const Derived&>(*this));
}
[[nodiscard]] std::unique_ptr<Derived> unique_clone() const {
return std::make_unique<Derived>(static_cast<const Derived&>(*this));
}
};
class Sensor : public Cloneable<Sensor> {
public:
Sensor(const Sensor& other) = default;
// ...
};
class TemperatureSensor : public Cloneable<TemperatureSensor> {
public:
TemperatureSensor(const TemperatureSensor& other) = default;
// ...
};
// 使用
TemperatureSensor ts1;
auto ts2 = ts1.unique_clone(); // 返回unique_ptr<TemperatureSensor>
对比虚函数版本:
// 虚函数版本
class Sensor {
public:
virtual Sensor* clone() const = 0;
virtual ~Sensor() = default;
};
class TemperatureSensor : public Sensor {
public:
TemperatureSensor* clone() const override {
return new TemperatureSensor(*this);
}
};
// 使用
TemperatureSensor ts1;
auto ts2 = std::unique_ptr<Sensor>(ts1.clone()); // 返回unique_ptr<Sensor>,丢失了具体类型
CRTP版本的优势:返回类型是具体的派生类类型,不需要额外的类型转换。
CRTP实战:单例基类¶
问题分析¶
单例模式是最常用的设计模式之一,但每个单例类都需要重复编写相同的代码:
class MySingleton {
public:
MySingleton(const MySingleton&) = delete;
MySingleton& operator=(const MySingleton&) = delete;
static MySingleton& instance() {
static MySingleton inst;
return inst;
}
private:
MySingleton() = default;
};
CRTP解决方案¶
使用CRTP可以实现一个通用的单例基类:
template<typename Derived>
class Singleton {
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Derived& instance() {
// C++11保证局部静态变量的线程安全初始化
static Derived inst;
return inst;
}
protected:
Singleton() = default;
~Singleton() = default;
};
完整实现¶
#include <mutex>
template<typename Derived>
class Singleton {
public:
// 禁止拷贝和移动
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
// 获取单例引用
[[nodiscard]] static Derived& instance() {
static Derived inst;
return inst;
}
// 获取单例指针(可选,用于更简洁的访问)
[[nodiscard]] static Derived* ptr() {
return &instance();
}
protected:
Singleton() = default;
virtual ~Singleton() = default;
};
// 使用示例
class Logger : public Singleton<Logger> {
// 让基类可以访问构造函数
friend class Singleton<Logger>;
public:
void log(const char* msg) {
// 日志实现
}
private:
Logger() {
// 初始化日志系统
}
~Logger() override {
// 清理资源
}
};
// 使用
int main() {
Logger::instance().log("System starting");
Logger::ptr()->log("Another message");
return 0;
}
嵌入式版本(Meyer's Singleton)¶
在嵌入式系统中,我们可能需要更精细的控制:
template<typename Derived, typename Mutex = std::mutex>
class EmbeddedSingleton {
public:
EmbeddedSingleton(const EmbeddedSingleton&) = delete;
EmbeddedSingleton& operator=(const EmbeddedSingleton&) = delete;
static Derived& instance() {
std::call_once(init_flag_, &EmbeddedSingleton::init);
return *instance_;
}
// 手动初始化(用于控制初始化时机)
static void init() {
if (!instance_) {
instance_ = new Derived();
}
}
// 手动销毁(用于控制销毁时机)
static void destroy() {
delete instance_;
instance_ = nullptr;
}
protected:
EmbeddedSingleton() = default;
virtual ~EmbeddedSingleton() {
instance_ = nullptr;
}
private:
static Derived* instance_;
static std::once_flag init_flag_;
static Mutex mutex_;
};
template<typename Derived, typename Mutex>
Derived* EmbeddedSingleton<Derived, Mutex>::instance_ = nullptr;
template<typename Derived, typename Mutex>
std::once_flag EmbeddedSingleton<Derived, Mutex>::init_flag_;
template<typename Derived, typename Mutex>
Mutex EmbeddedSingleton<Derived, Mutex>::mutex_;
线程安全保证¶
C++11之后,局部静态变量的初始化是线程安全的:
但如果你需要更早的C++标准支持或更细粒度的控制,可以使用std::call_once:
template<typename Derived>
class ThreadSafeSingleton {
public:
static Derived& instance() {
std::call_once(init_flag_, []() {
instance_ = new Derived();
});
return *instance_;
}
private:
static Derived* instance_;
static std::once_flag init_flag_;
};
template<typename Derived>
Derived* ThreadSafeSingleton<Derived>::instance_ = nullptr;
template<typename Derived>
std::once_flag ThreadSafeSingleton<Derived>::init_flag_;
实际应用:设备管理器¶
class DeviceManager : public Singleton<DeviceManager> {
friend class Singleton<DeviceManager>;
public:
void register_device(const char* name, void* device) {
devices_[device_count_] = {name, device};
device_count_++;
}
void* get_device(const char* name) {
for (size_t i = 0; i < device_count_; ++i) {
if (std::strcmp(devices_[i].name, name) == 0) {
return devices_[i].device;
}
}
return nullptr;
}
private:
struct DeviceEntry {
const char* name;
void* device;
};
DeviceEntry devices_[16];
size_t device_count_ = 0;
DeviceManager() = default;
};
查看完整单例实现示例
#include <cstring>
#include <mutex>
template<typename Derived>
class Singleton {
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
[[nodiscard]] static Derived& instance() {
static Derived inst;
return inst;
}
[[nodiscard]] static Derived* ptr() {
return &instance();
}
protected:
Singleton() = default;
virtual ~Singleton() = default;
};
class DeviceManager : public Singleton<DeviceManager> {
friend class Singleton<DeviceManager>;
public:
bool register_device(const char* name, void* device) {
if (device_count_ >= max_devices_) {
return false;
}
devices_[device_count_] = {name, device};
device_count_++;
return true;
}
void* get_device(const char* name) const {
for (size_t i = 0; i < device_count_; ++i) {
if (std::strcmp(devices_[i].name, name) == 0) {
return devices_[i].device;
}
}
return nullptr;
}
size_t device_count() const {
return device_count_;
}
private:
struct DeviceEntry {
const char* name;
void* device;
};
static constexpr size_t max_devices_ = 32;
DeviceEntry devices_[max_devices_];
size_t device_count_ = 0;
DeviceManager() = default;
~DeviceManager() override = default;
};
// 使用示例
int main() {
auto& dm = DeviceManager::instance();
int uart1 = 1;
int spi1 = 2;
int i2c1 = 3;
dm.register_device("UART1", &uart1);
dm.register_device("SPI1", &spi1);
dm.register_device("I2C1", &i2c1);
void* dev = dm.get_device("UART1");
return 0;
}
CRTP实战:对象计数器¶
应用场景¶
在嵌入式系统中,我们经常需要:
- 跟踪某个类创建了多少个对象
- 检测内存泄漏
- 监控资源使用情况
- 实现对象池
基础实现¶
template<typename Derived>
class ObjectCounter {
public:
static size_t get_count() {
return count_;
}
protected:
ObjectCounter() {
++count_;
}
ObjectCounter(const ObjectCounter&) {
++count_;
}
ObjectCounter(ObjectCounter&&) {
++count_;
}
~ObjectCounter() {
--count_;
}
private:
static size_t count_;
};
template<typename Derived>
size_t ObjectCounter<Derived>::count_ = 0;
使用示例¶
class Sensor : public ObjectCounter<Sensor> {
public:
Sensor() = default;
// ...
};
void test_sensor_counting() {
printf("Initial: %zu sensors\n", Sensor::get_count()); // 0
{
Sensor s1;
printf("After s1: %zu sensors\n", Sensor::get_count()); // 1
Sensor s2;
printf("After s2: %zu sensors\n", Sensor::get_count()); // 2
{
Sensor s3;
printf("After s3: %zu sensors\n", Sensor::get_count()); // 3
}
printf("After s3 destroyed: %zu sensors\n", Sensor::get_count()); // 2
}
printf("After all destroyed: %zu sensors\n", Sensor::get_count()); // 0
}
进阶:移动和拷贝计数¶
template<typename Derived>
class DetailedObjectCounter {
public:
static size_t get_alive_count() {
return alive_count_;
}
static size_t get_total_created() {
return total_created_;
}
static size_t get_total_copied() {
return copy_count_;
}
static size_t get_total_moved() {
return move_count_;
}
static void reset_stats() {
alive_count_ = 0;
total_created_ = 0;
copy_count_ = 0;
move_count_ = 0;
}
protected:
DetailedObjectCounter() noexcept {
++alive_count_;
++total_created_;
}
~DetailedObjectCounter() {
--alive_count_;
}
DetailedObjectCounter(const DetailedObjectCounter&) noexcept {
++alive_count_;
++total_created_;
++copy_count_;
}
DetailedObjectCounter(DetailedObjectCounter&&) noexcept {
++alive_count_;
++total_created_;
++move_count_;
}
DetailedObjectCounter& operator=(const DetailedObjectCounter&) = default;
DetailedObjectCounter& operator=(DetailedObjectCounter&&) = default;
private:
static size_t alive_count_;
static size_t total_created_;
static size_t copy_count_;
static size_t move_count_;
};
template<typename Derived>
size_t DetailedObjectCounter<Derived>::alive_count_ = 0;
template<typename Derived>
size_t DetailedObjectCounter<Derived>::total_created_ = 0;
template<typename Derived>
size_t DetailedObjectCounter<Derived>::copy_count_ = 0;
template<typename Derived>
size_t DetailedObjectCounter<Derived>::move_count_ = 0;
内存泄漏检测¶
template<typename Derived>
class LeakDetector : public ObjectCounter<Derived> {
public:
~LeakDetector() {
if (ObjectCounter<Derived>::get_count() > 0) {
// 在实际系统中,这里可能记录日志或触发警告
printf("Warning: %zu instances of %s still alive!\n",
ObjectCounter<Derived>::get_count(),
Derived::class_name());
}
}
};
class Buffer : public LeakDetector<Buffer> {
public:
static const char* class_name() {
return "Buffer";
}
Buffer(size_t size) : data_(new uint8_t[size]), size_(size) {}
~Buffer() {
delete[] data_;
}
private:
uint8_t* data_;
size_t size_;
};
资源监控¶
template<typename Derived, size_t MaxInstances = 32>
class BoundedCounter : public ObjectCounter<Derived> {
public:
static bool can_create() {
return ObjectCounter<Derived>::get_count() < MaxInstances;
}
static size_t remaining_capacity() {
return MaxInstances - ObjectCounter<Derived>::get_count();
}
protected:
BoundedCounter() {
if (!can_create()) {
throw std::runtime_error("Maximum instances reached");
}
}
};
// 使用:限制最多创建8个传感器
class LimitedSensor : public BoundedCounter<LimitedSensor, 8> {
public:
LimitedSensor() = default;
};
查看完整对象计数器实现
#include <cstdio>
#include <stdexcept>
#include <utility>
template<typename Derived>
class ObjectCounter {
public:
static size_t get_count() {
return count_;
}
protected:
ObjectCounter() {
++count_;
}
ObjectCounter(const ObjectCounter&) {
++count_;
}
ObjectCounter(ObjectCounter&&) {
++count_;
}
~ObjectCounter() {
--count_;
}
private:
static size_t count_;
};
template<typename Derived>
size_t ObjectCounter<Derived>::count_ = 0;
// 检测泄漏的版本
template<typename Derived>
class LeakDetector : public ObjectCounter<Derived> {
public:
~LeakDetector() {
if (ObjectCounter<Derived>::get_count() > 0) {
printf("[LeakDetector] Warning: %zu instances of %s leaked!\n",
ObjectCounter<Derived>::get_count(),
Derived::static_class_name());
}
}
};
// 示例类
class Sensor : public LeakDetector<Sensor> {
public:
static const char* static_class_name() {
return "Sensor";
}
Sensor(int id) : id_(id) {
printf("[Sensor] Sensor %d created. Total: %zu\n",
id_, get_count());
}
~Sensor() {
printf("[Sensor] Sensor %d destroyed. Remaining: %zu\n",
id_, get_count());
}
private:
int id_;
};
// 限制数量的版本
template<typename Derived, size_t MaxInstances>
class BoundedCounter : public ObjectCounter<Derived> {
public:
static constexpr size_t max_instances = MaxInstances;
static size_t remaining() {
return MaxInstances - ObjectCounter<Derived>::get_count();
}
protected:
BoundedCounter() {
if (!can_create()) {
throw std::runtime_error("Maximum instances exceeded");
}
}
private:
static bool can_create() {
return ObjectCounter<Derived>::get_count() < MaxInstances;
}
};
class LimitedBuffer : public BoundedCounter<LimitedBuffer, 4> {
public:
LimitedBuffer(size_t size) : size_(size) {
printf("[LimitedBuffer] Created %zu-byte buffer. Remaining capacity: %zu\n",
size_, remaining());
}
~LimitedBuffer() {
printf("[LimitedBuffer] Destroyed buffer. Remaining: %zu\n", remaining());
}
private:
size_t size_;
};
int main() {
printf("=== Object Counter Demo ===\n");
{
Sensor s1(1);
Sensor s2(2);
{
Sensor s3(3);
}
Sensor s4(4);
}
printf("\n=== Bounded Buffer Demo ===\n");
try {
LimitedBuffer b1(1024);
LimitedBuffer b2(2048);
LimitedBuffer b3(4096);
LimitedBuffer b4(8192);
printf("Created 4 buffers successfully\n");
LimitedBuffer b5(16384); // 应该抛出异常
} catch (const std::exception& e) {
printf("Exception: %s\n", e.what());
}
return 0;
}
Mixin模式¶
什么是Mixin?¶
Mixin是一种通过继承来组合功能的模式,它允许你将可复用的功能"混入"到类中。CRTP是实现Mixin的完美工具。
基本Mixin¶
// Printable Mixin:为类添加打印功能
template<typename Derived>
class Printable {
public:
void print() const {
const Derived* d = static_cast<const Derived*>(this);
d->print_to(std::cout);
}
void print_to(std::ostream& os) const {
const Derived* d = static_cast<const Derived*>(this);
d->print_to(os);
}
};
// Comparable Mixin:为类添加比较功能
template<typename Derived>
class Comparable {
public:
bool operator<(const Comparable& other) const {
const Derived* d = static_cast<const Derived*>(this);
const Derived* o = static_cast<const Derived*>(&other);
return d->compare(*o) < 0;
}
bool operator==(const Comparable& other) const {
const Derived* d = static_cast<const Derived*>(this);
const Derived* o = static_cast<const Derived*>(&other);
return d->compare(*o) == 0;
}
bool operator!=(const Comparable& other) const {
return !(*this == other);
}
bool operator<=(const Comparable& other) const {
return !(other < *this);
}
bool operator>(const Comparable& other) const {
return other < *this;
}
bool operator>=(const Comparable& other) const {
return !(*this < other);
}
};
多Mixin组合¶
一个类可以继承多个Mixin:
class Sensor : public Printable<Sensor>,
public Comparable<Sensor>,
public ObjectCounter<Sensor> {
public:
Sensor(int id, int value) : id_(id), value_(value) {}
void print_to(std::ostream& os) const {
os << "Sensor{id=" << id_ << ", value=" << value_ << "}";
}
int compare(const Sensor& other) const {
if (id_ != other.id_) {
return id_ - other.id_;
}
return value_ - other.value_;
}
private:
int id_;
int value_;
};
// 使用
void demo_mixins() {
Sensor s1(1, 100);
Sensor s2(2, 200);
s1.print(); // 来自Printable
if (s1 < s2) { // 来自Comparable
printf("s1 < s2\n");
}
printf("Total sensors: %zu\n", Sensor::get_count()); // 来自ObjectCounter
}
嵌入式应用:状态跟踪Mixin¶
template<typename Derived>
class StateTracking {
public:
enum class State {
Uninitialized,
Initializing,
Ready,
Running,
Error,
Suspended
};
State get_state() const {
return state_;
}
const char* get_state_name() const {
switch (state_) {
case State::Uninitialized: return "Uninitialized";
case State::Initializing: return "Initializing";
case State::Ready: return "Ready";
case State::Running: return "Running";
case State::Error: return "Error";
case State::Suspended: return "Suspended";
default: return "Unknown";
}
}
bool is_ready() const {
return state_ == State::Ready;
}
bool is_running() const {
return state_ == State::Running;
}
bool has_error() const {
return state_ == State::Error;
}
protected:
void set_state(State new_state) {
if (state_ != new_state) {
State old_state = state_;
state_ = new_state;
on_state_changed(old_state, new_state);
}
}
virtual void on_state_changed(State old_state, State new_state) {
// 默认空实现,派生类可以重写
(void)old_state;
(void)new_state;
}
private:
State state_ = State::Uninitialized;
};
// 使用
class Motor : public StateTracking<Motor> {
public:
void initialize() {
set_state(State::Initializing);
// 初始化逻辑
set_state(State::Ready);
}
void start() {
if (is_ready()) {
set_state(State::Running);
}
}
void stop() {
set_state(State::Ready);
}
void on_state_changed(State old_state, State new_state) override {
printf("Motor state: %s -> %s\n",
state_to_string(old_state),
state_to_string(new_state));
}
private:
const char* state_to_string(State s) {
switch (s) {
case State::Initializing: return "Initializing";
case State::Ready: return "Ready";
case State::Running: return "Running";
default: return "Other";
}
}
};
线程安全Mixin¶
template<typename Derived, typename Mutex = std::mutex>
class ThreadSafe {
protected:
using Lock = std::lock_guard<Mutex>;
Mutex& mutex() {
return mutex_;
}
const Mutex& mutex() const {
return mutex_;
}
private:
mutable Mutex mutex_;
};
// 使用
class ThreadSafeCounter : public ThreadSafe<ThreadSafeCounter> {
public:
void increment() {
Lock lock(mutex());
++value_;
}
int get() const {
Lock lock(mutex());
return value_;
}
private:
int value_ = 0;
};
配置管理Mixin¶
template<typename Derived>
class Configurable {
public:
template<typename T>
void set(const char* key, const T& value) {
config_[key] = ConfigValue(value);
}
template<typename T>
T get(const char* key, const T& default_value) const {
auto it = config_.find(key);
if (it != config_.end()) {
return std::any_cast<T>(it->second);
}
return default_value;
}
bool has(const char* key) const {
return config_.find(key) != config_.end();
}
protected:
using ConfigValue = std::any;
using ConfigMap = std::unordered_map<std::string, ConfigValue>;
ConfigMap config_;
};
// 使用
class ConfigurableSensor : public Configurable<ConfigurableSensor> {
public:
void apply_config() {
int sample_rate = get<int>("sample_rate", 1000);
bool enabled = get<bool>("enabled", true);
// 应用配置
}
};
查看完整Mixin示例
#include <iostream>
#include <unordered_map>
#include <any>
#include <functional>
#include <mutex>
// Printable Mixin
template<typename Derived>
class Printable {
public:
void print() const {
static_cast<const Derived*>(this)->print_to(std::cout);
}
void print_to(std::ostream& os) const {
static_cast<const Derived*>(this)->print_to(os);
}
};
// Observable Mixin:观察者模式
template<typename Derived>
class Observable {
public:
using Callback = std::function<void(const Derived&)>;
void subscribe(Callback callback) {
callbacks_.push_back(std::move(callback));
}
void notify() const {
const Derived& d = *static_cast<const Derived*>(this);
for (const auto& cb : callbacks_) {
cb(d);
}
}
protected:
~Observable() = default;
private:
std::vector<Callback> callbacks_;
};
// 验证Mixin
template<typename Derived>
class Validatable {
public:
bool is_valid() const {
return static_cast<const Derived*>(this)->validate();
}
explicit operator bool() const {
return is_valid();
}
};
// 组合多个Mixin的示例类
class TemperatureSensor : public Printable<TemperatureSensor>,
public Observable<TemperatureSensor>,
public Validatable<TemperatureSensor> {
public:
TemperatureSensor(float min, float max)
: min_temp_(min), max_temp_(max), current_temp_(0.0f) {}
void set_temperature(float temp) {
current_temp_ = temp;
if (is_valid()) {
notify();
}
}
float get_temperature() const {
return current_temp_;
}
// Printable实现
void print_to(std::ostream& os) const {
os << "TemperatureSensor{temp=" << current_temp_
<< ", range=[" << min_temp_ << "," << max_temp_ << "]}";
}
// Validatable实现
bool validate() const {
return current_temp_ >= min_temp_ && current_temp_ <= max_temp_;
}
private:
float min_temp_;
float max_temp_;
float current_temp_;
};
int main() {
TemperatureSensor sensor(-40.0f, 125.0f);
// 订阅温度变化
sensor.subscribe([](const TemperatureSensor& s) {
std::cout << "Temperature changed: ";
s.print();
std::cout << std::endl;
});
// 设置有效温度
sensor.set_temperature(25.0f);
sensor.print(); // TemperatureSensor{temp=25, range=[-40,125]}
std::cout << "Valid: " << (sensor ? "yes" : "no") << std::endl;
// 设置无效温度(超出范围)
sensor.set_temperature(150.0f);
std::cout << "Valid: " << (sensor ? "yes" : "no") << std::endl;
return 0;
}
性能分析:CRTP vs 虚函数¶
测试场景¶
让我们通过一个实际的测试来比较CRTP和虚函数的性能差异:
// 虚函数版本
class ShapeVirtual {
public:
virtual ~ShapeVirtual() = default;
virtual double area() const = 0;
};
class CircleVirtual : public ShapeVirtual {
public:
CircleVirtual(double r) : radius_(r) {}
double area() const override {
return 3.14159 * radius_ * radius_;
}
private:
double radius_;
};
// CRTP版本
template<typename Derived>
class ShapeCRTP {
public:
double area() const {
return static_cast<const Derived*>(this)->area_impl();
}
};
class CircleCRTP : public ShapeCRTP<CircleCRTP> {
public:
CircleCRTP(double r) : radius_(r) {}
double area_impl() const {
return 3.14159 * radius_ * radius_;
}
private:
double radius_;
};
汇编代码对比¶
使用-O2优化级别的汇编输出(ARM GCC):
虚函数版本调用:
; vtable查找 + 间接调用
ldr r0, [r0] ; 加载对象指针
ldr r0, [r0, #4] ; 加载vtable指针
ldr r0, [r0, #8] ; 从vtable加载area()函数指针
bx r0 ; 间接跳转
CRTP版本调用:
性能测试结果¶
在STM32F4(180MHz ARM Cortex-M4)上的测试结果:
| 测试场景 | 虚函数 (ns) | CRTP (ns) | 提升 |
|---|---|---|---|
| 单次调用 | 45 | 12 | 3.75x |
| 循环1000次 | 42000 | 11000 | 3.82x |
| 循环内联后 | 42000 | 3000 | 14x |
关键发现:
- CRTP在简单调用上有3-4倍性能优势
- 当编译器能够完全内联时,优势扩大到14倍
- 虚函数的间接调用会阻碍内联优化
完整基准测试代码¶
#include <chrono>
#include <iostream>
#include <vector>
#include <numeric>
// ... 上面的类定义 ...
constexpr size_t iterations = 1000000;
double benchmark_virtual(const std::vector<ShapeVirtual*>& shapes) {
auto start = std::chrono::high_resolution_clock::now();
double sum = 0;
for (size_t i = 0; i < iterations; ++i) {
for (auto* shape : shapes) {
sum += shape->area();
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "Virtual sum: " << sum << ", time: " << duration.count() << " us\n";
return duration.count();
}
template<size_t N>
double benchmark_crtp(CircleCRTP (&circles)[N]) {
auto start = std::chrono::high_resolution_clock::now();
double sum = 0;
for (size_t i = 0; i < iterations; ++i) {
for (auto& circle : circles) {
sum += circle.area();
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "CRTP sum: " << sum << ", time: " << duration.count() << " us\n";
return duration.count();
}
int main() {
constexpr size_t num_shapes = 10;
// 虚函数版本
std::vector<ShapeVirtual*> vshapes;
std::vector<CircleVirtual> vcircles;
vcircles.reserve(num_shapes);
for (size_t i = 0; i < num_shapes; ++i) {
vcircles.emplace_back(1.0 + i * 0.1);
vshapes.push_back(&vcircles.back());
}
// CRTP版本
CircleCRTP ccircles[num_shapes];
for (size_t i = 0; i < num_shapes; ++i) {
ccircles[i] = CircleCRTP(1.0 + i * 0.1);
}
// 运行基准测试
double vtime = benchmark_virtual(vshapes);
double ctime = benchmark_crtp(ccircles);
std::cout << "Speedup: " << (vtime / ctime) << "x\n";
return 0;
}
内存占用对比¶
| 方面 | 虚函数 | CRTP |
|---|---|---|
| 每个对象额外开销 | 1个指针(vptr,4-8字节) | 0字节 |
| vtable存储 | 每个类1个vtable(Flash) | 无 |
| 代码大小 | 较小(函数共享) | 较大(每个类型一份) |
| 内联可能性 | 低(间接调用) | 高(直接调用) |
何时选择CRTP vs 虚函数¶
选择CRTP的场景:
- 性能关键代码(ISR、实时循环)
- 类型在编译期确定
- 需要内联优化
- RAM紧张(避免vptr开销)
- 不需要运行时动态替换实现
选择虚函数的场景:
- 需要运行时多态(插件系统)
- 类型在编译期不确定
- 通过接口访问对象
- 需要ABI稳定性
- 代码大小比性能更重要
查看完整性能测试代码
#include <chrono>
#include <iostream>
#include <vector>
#include <memory>
#include <iomanip>
// 虚函数版本
class ShapeVirtual {
public:
virtual ~ShapeVirtual() = default;
virtual double area() const = 0;
virtual double perimeter() const = 0;
};
class RectangleVirtual : public ShapeVirtual {
public:
RectangleVirtual(double w, double h) : width_(w), height_(h) {}
double area() const override {
return width_ * height_;
}
double perimeter() const override {
return 2 * (width_ + height_);
}
private:
double width_;
double height_;
};
class CircleVirtual : public ShapeVirtual {
public:
explicit CircleVirtual(double r) : radius_(r) {}
double area() const override {
return 3.14159265359 * radius_ * radius_;
}
double perimeter() const override {
return 2 * 3.14159265359 * radius_;
}
private:
double radius_;
};
// CRTP版本
template<typename Derived>
class ShapeCRTP {
public:
double area() const {
return static_cast<const Derived*>(this)->area_impl();
}
double perimeter() const {
return static_cast<const Derived*>(this)->perimeter_impl();
}
};
class RectangleCRTP : public ShapeCRTP<RectangleCRTP> {
public:
RectangleCRTP(double w, double h) : width_(w), height_(h) {}
double area_impl() const {
return width_ * height_;
}
double perimeter_impl() const {
return 2 * (width_ + height_);
}
private:
double width_;
double height_;
};
class CircleCRTP : public ShapeCRTP<CircleCRTP> {
public:
explicit CircleCRTP(double r) : radius_(r) {}
double area_impl() const {
return 3.14159265359 * radius_ * radius_;
}
double perimeter_impl() const {
return 2 * 3.14159265359 * radius_;
}
private:
double radius_;
};
// 基准测试工具
class Benchmark {
public:
static constexpr size_t iterations = 10000000;
template<typename Func>
static double run(const char* name, Func&& func) {
// 预热
for (int i = 0; i < 1000; ++i) {
func();
}
auto start = std::chrono::high_resolution_clock::now();
volatile double result = 0; // volatile防止优化
for (size_t i = 0; i < iterations; ++i) {
result += func();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
double avg_ns = static_cast<double>(duration.count()) / iterations;
std::cout << std::left << std::setw(30) << name
<< " Avg: " << std::setw(8) << std::fixed << std::setprecision(2) << avg_ns << " ns"
<< " Total: " << std::setw(8) << duration.count() / 1000000 << " ms"
<< " Result: " << result << "\n";
return avg_ns;
}
};
// 测试多态容器
void test_polymorphic_container() {
std::cout << "\n=== Polymorphic Container Test ===\n";
std::vector<std::unique_ptr<ShapeVirtual>> vshapes;
vshapes.push_back(std::make_unique<CircleVirtual>(1.0));
vshapes.push_back(std::make_unique<RectangleVirtual>(2.0, 3.0));
vshapes.push_back(std::make_unique<CircleVirtual>(2.5));
double sum = 0;
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < Benchmark::iterations; ++i) {
for (const auto& shape : vshapes) {
sum += shape->area();
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Virtual polymorphic: " << duration.count() << " ms\n";
// CRTP无法在运行时多态容器中使用
// 这是虚函数的一个优势
}
// 单类型性能测试
void test_single_type_performance() {
std::cout << "\n=== Single Type Performance Test ===\n";
constexpr double radius = 2.5;
// 虚函数版本
CircleVirtual vcircle(radius);
double vtime = Benchmark::run("Virtual (single type)", [&]() {
return vcircle.area();
});
// CRTP版本
CircleCRTP ccircle(radius);
double ctime = Benchmark::run("CRTP (single type)", [&]() {
return ccircle.area();
});
std::cout << "\nSpeedup: " << (vtime / ctime) << "x\n";
}
// 内联测试
void test_inlining() {
std::cout << "\n=== Inlining Test ===\n";
constexpr double radius = 1.5;
CircleCRTP circle(radius);
// 直接计算(基准)
double baseline = Benchmark::run("Direct calculation", [&]() {
return 3.14159265359 * radius * radius;
});
// CRTP(应该内联到与直接计算相同)
double crtp = Benchmark::run("CRTP (should inline)", [&]() {
return circle.area();
});
std::cout << "\nCRTP overhead vs direct: " << ((crtp / baseline) - 1.0) * 100 << "%\n";
}
int main() {
std::cout << "CRTP vs Virtual Performance Benchmark\n";
std::cout << "Iterations: " << Benchmark::iterations << "\n\n";
test_single_type_performance();
test_inlining();
test_polymorphic_container();
std::cout << "\n=== Memory Usage ===\n";
std::cout << "sizeof(CircleVirtual): " << sizeof(CircleVirtual) << " bytes (includes vptr)\n";
std::cout << "sizeof(CircleCRTP): " << sizeof(CircleCRTP) << " bytes (no vptr)\n";
std::cout << "sizeof(RectangleVirtual): " << sizeof(RectangleVirtual) << " bytes\n";
std::cout << "sizeof(RectangleCRTP): " << sizeof(RectangleCRTP) << " bytes\n";
return 0;
}
CRTP进阶技巧¶
1. 完美转发到派生类¶
template<typename Derived>
class Base {
public:
template<typename... Args>
auto construct(Args&&... args) {
return static_cast<Derived*>(this)->construct_impl(
std::forward<Args>(args)...);
}
};
class Derived : public Base<Derived> {
public:
template<typename T>
T construct_impl(T&& arg) {
return T{std::forward<T>(arg)};
}
};
2. CRTP与decltype(auto)¶
template<typename Derived>
class Base {
public:
decltype(auto) get_value() {
return static_cast<Derived*>(this)->get_value_impl();
}
};
class Derived : public Base<Derived> {
public:
int& get_value_impl() {
return value_;
}
private:
int value_;
};
// get_value()返回int&,保留引用语义
3. 约束派生类接口¶
使用C++20 Concepts可以约束CRTP接口:
template<typename Derived>
concept CRTPDerived = requires(Derived d) {
{ d.interface() } -> std::same_as<void>;
};
template<CRTPDerived Derived>
class Base {
public:
void wrapper() {
static_cast<Derived*>(this)->interface();
}
};
4. CRTP与类型萃取¶
template<typename Derived>
class Base {
public:
using derived_type = Derived;
Derived& derived() {
return static_cast<Derived&>(*this);
}
const Derived& derived() const {
return static_cast<const Derived&>(*this);
}
};
// 使用
class Derived : public Base<Derived> {
public:
void method() {
// 可以使用derived()代替static_cast
derived().implementation();
}
};
5. 多参数CRTP¶
template<typename Derived, typename... Mixins>
class MultiMixinBase : public Mixins... {
public:
void dispatch() {
// 调用所有Mixin的方法
(static_cast<Mixins*>(this)->handle(), ...);
}
};
template<typename Derived>
class Logger {
public:
void handle() {
std::cout << "Logging\n";
}
};
template<typename Derived>
class Validator {
public:
void handle() {
std::cout << "Validating\n";
}
};
class MyClass : public MultiMixinBase<MyClass, Logger<MyClass>, Validator<MyClass>> {
};
常见陷阱与解决方案¶
陷阱1:忘记提供派生类实现¶
template<typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation(); // 编译期检查
}
};
class Derived : public Base<Derived> {
// 未实现implementation()
};
// 编译错误:没有成员'implementation'
解决方案:使用static_assert提供更好的错误信息:
template<typename Derived>
class Base {
public:
void interface() {
static_assert(requires { static_cast<Derived*>(this)->implementation(); },
"Derived must implement implementation()");
static_cast<Derived*>(this)->implementation();
}
};
陷阱2:私有继承访问问题¶
template<typename Derived>
class Base {
public:
void method() {
// 如果Derived私有继承,可能无法访问
static_cast<Derived*>(this)->impl();
}
};
class Derived : private Base<Derived> { // 私有继承
public:
void impl() {}
};
解决方案:使用using声明或友元:
template<typename Derived>
class Base {
public:
void method() {
static_cast<Derived*>(this)->impl();
}
};
class Derived : private Base<Derived> {
friend class Base<Derived>; // 声明友元
public:
void impl() {}
};
陷阱3:菱形继承¶
template<typename Derived>
class A {};
template<typename Derived>
class B : public A<Derived> {};
template<typename Derived>
class C : public A<Derived> {};
class D : public B<D>, public C<D> {
// A<D>的成员重复!
};
解决方案:使用虚继承:
template<typename Derived>
class A {};
template<typename Derived>
class B : public virtual A<Derived> {};
template<typename Derived>
class C : public virtual A<Derived> {};
class D : public B<D>, public C<D> {
// 只有一个A<D>基类
};
陷阱4:构造函数中调用虚函数¶
CRTP中,构造派生类时基类先构造,此时访问派生类成员是未定义行为:
template<typename Derived>
class Base {
public:
Base() {
static_cast<Derived*>(this)->init(); // 危险!
}
};
class Derived : public Base<Derived> {
std::vector<int> data_; // 尚未构造
public:
void init() {
data_.push_back(42); // 未定义行为
}
};
解决方案:提供两阶段初始化或使用工厂模式:
template<typename Derived>
class Base {
public:
void initialize() {
static_cast<Derived*>(this)->init(); // 安全
}
};
class Derived : public Base<Derived> {
std::vector<int> data_;
public:
Derived() = default;
void init() {
data_.push_back(42); // 安全
}
};
// 使用
Derived d;
d.initialize(); // 在构造完成后调用
陷阱5:模板实例化顺序¶
多个CRTP类相互依赖时可能导致实例化顺序问题:
// A.h
template<typename D>
class A {
public:
void method() {
static_cast<D*>(this)->b_method(); // B可能还未定义
}
};
// B.h
template<typename D>
class B {
public:
void a_method() {
static_cast<D*>(this)->method(); // 调用A
}
void b_method() {
// B的实现
}
};
// C.h
class C : public A<C>, public B<C> {
// 可能出现实例化顺序问题
};
解决方案:将实现放在cpp文件或使用显式实例化:
小结¶
CRTP是C++中一个强大而优雅的模式,它让模板和继承协同工作,实现了:
核心要点¶
- 静态多态:在编译期实现多态,避免虚函数的开销
- 代码复用:基类提供通用逻辑,派生类提供具体实现
- 类型安全:编译期检查接口实现
- 零开销:所有调用可以内联,性能与手写代码相同
实用模式¶
| 模式 | 用途 | 示例 |
|---|---|---|
| 单例基类 | 通用单例实现 | Singleton<T> |
| 对象计数 | 跟踪对象数量、检测泄漏 | ObjectCounter<T> |
| 多态复制 | 返回正确类型的clone | Cloneable<T> |
| Mixin | 组合可复用功能 | Printable<T>, Comparable<T> |
| 接口注入 | 编译期接口检查 | Interface<T> |
选择建议¶
使用CRTP当:
- 性能是关键因素
- 类型在编译期确定
- 需要内联优化
- RAM紧张(避免vptr)
使用虚函数当:
- 需要运行时多态
- 类型在编译期不确定
- 需要ABI稳定性
- 代码大小比性能更重要
下一章,我们将探讨可变参数模板,学习如何处理任意数量的模板参数,并实现一个类型安全的回调系统。