C++(C++17/20)最佳工厂写法和SLAM应用综合示例

现代 C++(C++17/20)下的最佳工厂写法


一、现代 C++ 工厂设计的基本原则

在 C++17/20 下,好的工厂写法通常满足:

  1. RAII + 明确所有权

    • 返回 std::unique_ptr<T> 为默认
  2. switch / 无 RTTI

  3. 支持扩展而不修改(OCP)

  4. 构造逻辑与业务逻辑分离

  5. 尽量零运行期成本或可控成本


二、最推荐写法:注册式工厂(Registry-based Factory)

90% 工程项目的首选

1. 基本结构

cpp 复制代码
class Base {
public:
    virtual ~Base() = default;
    virtual void run() = 0;
};

using Creator = std::function<std::unique_ptr<Base>()>;
cpp 复制代码
class Factory {
public:
    static Factory& instance() {
        static Factory f;
        return f;
    }

    void registerCreator(std::string key, Creator creator) {
        creators_.emplace(std::move(key), std::move(creator));
    }

    std::unique_ptr<Base> create(const std::string& key) const {
        return creators_.at(key)();
    }

private:
    std::unordered_map<std::string, Creator> creators_;
};

2. 自动注册(推荐)

cpp 复制代码
template<typename T>
struct AutoRegister {
    AutoRegister(const std::string& key) {
        Factory::instance().registerCreator(
            key, [] { return std::make_unique<T>(); }
        );
    }
};
cpp 复制代码
class ImplA : public Base {
public:
    void run() override {}
};

static AutoRegister<ImplA> regA("A");

优点

  • 零侵入
  • 插件友好
  • 新增类型无需改工厂

3. 为什么这是"现代"写法?

  • 使用 unique_ptr 明确所有权
  • Lambda 消除样板代码
  • static 局部变量保证线程安全(C++11+)
  • 完全 OCP

三、带参数的现代工厂写法(非常常见)

1. 问题

构造函数有参数:

cpp 复制代码
ImplA(int w, double s);

2. 推荐方案:参数对象(Parameter Object)

cpp 复制代码
struct Config {
    int width;
    double scale;
};
cpp 复制代码
using Creator = std::function<std::unique_ptr<Base>(const Config&)>;
cpp 复制代码
class Factory {
public:
    void registerCreator(std::string key, Creator c) {
        creators_[key] = std::move(c);
    }

    std::unique_ptr<Base> create(
        const std::string& key, const Config& cfg) const {
        return creators_.at(key)(cfg);
    }

private:
    std::unordered_map<std::string, Creator> creators_;
};
cpp 复制代码
Factory::instance().registerCreator(
    "A", [](const Config& cfg) {
        return std::make_unique<ImplA>(cfg.width, cfg.scale);
    });

工程价值

  • 构造参数可扩展
  • 工厂接口稳定
  • 不破坏 ABI

四、C++20 改进:Concept + 工厂

适合大型工程 / 库级代码

cpp 复制代码
template<typename T>
concept Product =
    std::derived_from<T, Base> &&
    std::default_initializable<T>;
cpp 复制代码
template<Product T>
void registerType(std::string key) {
    Factory::instance().registerCreator(
        std::move(key), [] {
            return std::make_unique<T>();
        });
}

收益

  • 编译期约束
  • 错误更早暴露
  • 接口自文档化

五、高性能版本:无 std::function

当工厂在热路径中被频繁调用:

1. 问题

  • std::function 可能引入堆分配
  • 虚调用 + 间接跳转

2. 解决方案:函数指针 / 模板表

cpp 复制代码
using Creator = std::unique_ptr<Base>(*)();
cpp 复制代码
template<typename T>
std::unique_ptr<Base> createImpl() {
    return std::make_unique<T>();
}
cpp 复制代码
creators_["A"] = &createImpl<ImplA>;

权衡

  • 性能更优
  • 灵活性略降
  • 适合内核模块

六、编译期工厂(零运行期开销)

当类型集合在编译期固定

cpp 复制代码
template<typename T>
std::unique_ptr<Base> create() {
    return std::make_unique<T>();
}

调用方:

cpp 复制代码
auto obj = create<ImplA>();

适合

  • 算法内核
  • 无运行期配置
  • 性能敏感路径

七、工厂 + 插件系统(dlopen)

典型结构:

text 复制代码
Factory (主程序)
   ↑
PluginA.so → register("A")
PluginB.so → register("B")

插件加载时执行:

cpp 复制代码
extern "C" void registerPlugin() {
    Factory::instance().registerCreator(
        "A", [] { return std::make_unique<ImplA>(); });
}

这是现代大型 C++ 系统最常见的用法之一。


八、现代 C++ 工厂的反例(请避免)

返回裸指针

cpp 复制代码
Base* create();

工厂中塞业务逻辑

cpp 复制代码
createAndRunAndValidate();

构造参数散落

cpp 复制代码
create(int, int, double, bool);

九、快速选型总结

场景 推荐方案
配置驱动 注册式工厂
插件系统 自动注册
性能敏感 函数指针工厂
编译期固定 模板工厂
API/SDK Concept + Factory

十、总结

现代 C++ 的工厂模式,本质是:
用类型系统 + RAII + 注册机制,
将"变化"限制在最小边界内。


一个真实 SLAM 模块的完整工厂实现

示例模块:FeatureExtractor


一、模块目标与设计边界

1. 目标

  • 支持多种特征提取算法(ORB / FAST / SuperPoint)
  • 通过配置文件选择实现
  • 不修改核心逻辑即可新增算法
  • 生命周期与 SLAM 系统一致

2. 架构位置(典型 SLAM)

text 复制代码
Frontend
 ├── FeatureExtractor  ← 工厂创建
 ├── FeatureMatcher
 └── Tracker

FeatureExtractor 是典型"策略可替换模块"


二、接口定义

feature_extractor.h

cpp 复制代码
#pragma once
#include <vector>
#include <opencv2/core.hpp>

struct Feature {
    cv::KeyPoint keypoint;
    cv::Mat descriptor;
};

class FeatureExtractor {
public:
    virtual ~FeatureExtractor() = default;

    virtual std::vector<Feature>
    extract(const cv::Mat& image) = 0;
};

设计要点

  • 纯接口
  • 无构造参数
  • 不暴露具体实现细节

三、参数对象(Config)

extractor_config.h

cpp 复制代码
#pragma once
#include <string>

struct ExtractorConfig {
    std::string type;   // "ORB" / "FAST" / ...
    int max_features = 1000;
    float scale_factor = 1.2f;
    int levels = 8;
};

关键工程点

  • 构造参数集中
  • 新增参数不破坏工厂接口
  • 易于 YAML / JSON 映射

四、具体实现(变化层)

orb_extractor.h

cpp 复制代码
#pragma once
#include "feature_extractor.h"
#include "extractor_config.h"

class ORBExtractor final : public FeatureExtractor {
public:
    explicit ORBExtractor(const ExtractorConfig& cfg);

    std::vector<Feature>
    extract(const cv::Mat& image) override;

private:
    int max_features_;
    float scale_factor_;
    int levels_;
};

orb_extractor.cpp

cpp 复制代码
#include "orb_extractor.h"

ORBExtractor::ORBExtractor(const ExtractorConfig& cfg)
    : max_features_(cfg.max_features),
      scale_factor_(cfg.scale_factor),
      levels_(cfg.levels) {}

std::vector<Feature>
ORBExtractor::extract(const cv::Mat& image) {
    std::vector<Feature> features;
    // ORB extraction logic...
    return features;
}

五、工厂定义(核心)

feature_extractor_factory.h

cpp 复制代码
#pragma once
#include <memory>
#include <unordered_map>
#include <functional>
#include <string>

#include "feature_extractor.h"
#include "extractor_config.h"

class FeatureExtractorFactory {
public:
    using Creator =
        std::function<std::unique_ptr<FeatureExtractor>(
            const ExtractorConfig&)>;

    static FeatureExtractorFactory& instance();

    void registerCreator(const std::string& type, Creator creator);

    std::unique_ptr<FeatureExtractor>
    create(const ExtractorConfig& cfg) const;

private:
    FeatureExtractorFactory() = default;

    std::unordered_map<std::string, Creator> creators_;
};

feature_extractor_factory.cpp

cpp 复制代码
#include "feature_extractor_factory.h"
#include <stdexcept>

FeatureExtractorFactory&
FeatureExtractorFactory::instance() {
    static FeatureExtractorFactory factory;
    return factory;
}

void FeatureExtractorFactory::registerCreator(
    const std::string& type, Creator creator) {
    creators_[type] = std::move(creator);
}

std::unique_ptr<FeatureExtractor>
FeatureExtractorFactory::create(const ExtractorConfig& cfg) const {
    auto it = creators_.find(cfg.type);
    if (it == creators_.end()) {
        throw std::runtime_error(
            "Unknown FeatureExtractor type: " + cfg.type);
    }
    return it->second(cfg);
}

六、自动注册(工程关键技巧)

extractor_register.h

cpp 复制代码
#pragma once
#include "feature_extractor_factory.h"

template<typename T>
struct ExtractorRegistrar {
    ExtractorRegistrar(const std::string& type) {
        FeatureExtractorFactory::instance()
            .registerCreator(
                type,
                [](const ExtractorConfig& cfg) {
                    return std::make_unique<T>(cfg);
                });
    }
};

orb_register.cpp

cpp 复制代码
#include "orb_extractor.h"
#include "extractor_register.h"

static ExtractorRegistrar<ORBExtractor> reg_orb("ORB");

重要工程特性

  • 无需修改工厂

  • 新算法只需:

    • 新类
    • .cpp 注册文件

七、系统使用方式

cpp 复制代码
ExtractorConfig cfg;
cfg.type = "ORB";
cfg.max_features = 1500;

auto extractor =
    FeatureExtractorFactory::instance().create(cfg);

auto features = extractor->extract(image);

此处 Frontend 完全不知道 ORB 的存在


八、为什么这是"真实 SLAM 工厂实现"

1. 满足真实工程需求

  • 算法频繁切换
  • 配置驱动
  • 生命周期清晰
  • 可测试、可扩展

2. 与 SLAM 实际复杂度匹配

  • 不引入 DI 框架
  • 不过度模板化
  • 不牺牲可读性

3. 易于扩展为插件系统

text 复制代码
libslam_core.so
liborb_extractor.so
libsuperpoint_extractor.so

每个插件在加载时完成注册。


九、常见坑(真实踩坑总结)

1. 静态初始化顺序问题

问题: 注册对象在工厂之前初始化

方案: 使用 instance() 的局部静态


2. 工厂里塞逻辑

问题: 工厂决定参数

方案: 参数由 Config 决定


3. 返回裸指针

问题: 生命周期不明确

方案: std::unique_ptr


十、总结

在真实 SLAM 系统中,
工厂不是"设计模式练习",
而是"算法可演化性的基础设施"。


相关推荐
csbysj20202 小时前
SQLite Select 语句
开发语言
_WndProc2 小时前
【Python】方程计算器
开发语言·python
Q741_1472 小时前
C++ 队列 宽度优先搜索 BFS 力扣 662. 二叉树最大宽度 每日一题
c++·算法·leetcode·bfs·宽度优先
会游泳的石头2 小时前
Java 异步事务完成后的监听器:原理、实现与应用场景
java·开发语言·数据库
csdn_aspnet2 小时前
C++跨平台开发:工程难题与解决方案深度解析
c++
黎雁·泠崖2 小时前
Java字符串进阶:StringBuilder+StringJoiner
java·开发语言
我的offer在哪里2 小时前
技术实战:用 Python 脚本高效采集与分析手机操作日志
开发语言·python·智能手机
余衫马2 小时前
在Win10下编译 Poppler
c++·windows·qt·pdf·poppler
工程师0072 小时前
C#中的AutoUpdater自动更新类
开发语言·c#·自动更新开源库·autoupdate