示例1:类型擦除实现通用容器
#include
#include
#include
// 基类接口(动态多态)
class Shape {
public:
virtual void draw() const = 0;
virtual ~Shape() = default;
};
// 具体类型(静态多态)
class Circle : public Shape {
public:
void draw() const override { std::cout << "Drawing Circle\n"; }
};
class Square : public Shape {
public:
void draw() const override { std::cout << "Drawing Square\n"; }
};
// 类型擦除容器
class ShapeContainer {
private:
struct Concept {
virtual void draw() const = 0;
virtual ~Concept() = default;
};
template<typename T>
struct Model final : Concept {
T obj;
Model(const T& t) : obj(t) {}
void draw() const override { obj.draw(); }
};
std::vector<std::unique_ptr<Concept>> data;
public:
template<typename T>
void add(const T& shape) {
data.emplace_back(std::make_unique<Model<T>>(shape));
}
void drawAll() const {
for (const auto& elem : data) {
elem->draw();
}
}
};
int main() {
ShapeContainer container;
container.add(Circle{}); // 静态多态实例
container.add(Square{}); // 静态多态实例
container.drawAll(); // 动态调用所有元素的draw方法
return 0;
}
示例2:std::function实现桥接
#include
#include
// 桥接接口
class Logger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~Logger() = default;
};
// 具体日志器(静态多态)
class ConsoleLogger : public Logger {
public:
void log(const std::string& msg) override {
std::cout << "Console: " << msg << "\n";
}
};
class FileLogger : public Logger {
public:
void log(const std::string& msg) override {
std::cout << "File: " << msg << "\n";
}
};
// 桥接工厂(模板注册)
template<typename LoggerType>
void registerLogger(const std::string& name, std::function<std::unique_ptr<Logger>()> creator) {
// 注册逻辑(此处简化为直接创建)
auto logger = creator();
logger->log("Registered: " + name);
}
int main() {
// 动态选择日志器
registerLogger<ConsoleLogger>("console", [] { return std::make_unique<ConsoleLogger>(); });
registerLogger<FileLogger>("file", [] { return std::make_unique<FileLogger>(); });
// 运行时动态调用
std::unique_ptr<Logger> console = std::make_unique<ConsoleLogger>();
console->log("Hello, World!");
return 0;
}
示例3:模板与虚函数的混合使用
#include
#include
// 基类(动态多态)
class Animal {
public:
virtual void speak() const = 0;
virtual ~Animal() = default;
};
// 模板派生类(静态多态)
template<typename T>
class Speaker : public Animal {
T message;
public:
Speaker(const T& msg) : message(msg) {}
void speak() const override { std::cout << message << "\n"; }
};
int main() {
std::vector<std::unique_ptr<Animal>> animals;
// 静态实例化不同模板参数
animals.emplace_back(std::make_unique<Speaker<std::string>>("Meow"));
animals.emplace_back(std::make_unique<Speaker<const char*>>("Woof"));
animals.emplace_back(std::make_unique<Speaker<int>>(42)); // 输出数字
// 动态调用
for (const auto& animal : animals) {
animal->speak();
}
return 0;
}
类型擦除容器测试
输出:
Drawing Circle
Drawing Square
桥接工厂测试
输出:
Registered: console
Registered: file
Console: Hello, World!
混合多态测试
输出:
Meow
Woof
42
题目1:关于std::function
的实现原理,以下哪些说法正确?
A) 内部基于虚函数表实现类型擦除
B) 支持存储任意可调用对象(函数指针/lambda/仿函数)
C) 存储时需要拷贝可调用对象
D) 调用时通过operator()
隐式转换到目标类型
答案:B, C
详解:
std::function
通过类型擦除实现,但并非直接使用虚函数表(现代实现通常用小对象优化或虚表组合)。operator()
直接调用存储的可调用对象(D错误)。题目2:类型擦除的核心目的是什么?
A) 实现静态多态
B) 隐藏具体类型信息
C) 允许异构对象集合
D) 提升运行时性能
答案:B, C
详解:
std::vector>
)(C正确)。题目3:以下哪种方式无法实现类型擦除?
A) 继承自虚基类
B) 使用std::any
C) 函数指针强制转换
D) std::variant
答案:C, D
详解:
std::any
内部通过类型擦除存储任意类型(B正确)。std::variant
是类型安全的联合体,非擦除(D错误)。题目4:关于动态多态与静态多态的对比,正确的是?
A) 动态多态通过虚函数实现,静态多态通过模板
B) 动态多态编译期绑定,静态多态运行时绑定
C) 动态多态适合异质对象集合,静态多态适合同质操作
D) 动态多态无代码膨胀,静态多态有代码膨胀
答案:A, C
详解:
std::vector
),静态多态适合同质算法(如std::sort
)(C正确)。题目5:实现可调用对象包装器时,哪种方式无法保留参数类型信息?
A) std::function
B) 手动虚函数派发
C) std::bind
D) 直接函数指针存储
答案:D
详解:
std::function
和手动派发可保留参数类型(A/B正确)。std::bind
通过占位符保留参数类型(C正确)。题目6:以下代码存在什么问题?
std::vector<std::function<void()>> tasks;
tasks.push_back([]{ std::cout << "Task1"; });
tasks.push_back(std::bind(&Foo::bar, Foo()));
A) Foo
缺少默认构造函数
B) std::bind
返回类型不兼容
C) Foo::bar
应为静态成员
D) std::function
无法存储成员函数
答案:A
详解:
std::bind(&Foo::bar, Foo())
需要Foo
有默认构造函数(A正确)。std::function
可存储成员函数(D错误)。Foo::bar
的返回值(B错误)。题目7:类型擦除的常见实现模式有哪些?
A) 小对象优化(Small Object Optimization)
B) 虚函数表转发
C) Pimpl idiom
D) CRTP
答案:A, B
详解:
题目8:以下哪种情况会导致std::function
拷贝代价高昂?
A) 存储小型lambda
B) 存储成员函数指针
C) 存储大对象仿函数
D) 存储空函数
答案:C
详解:
题目9:实现静态多态的常见技术有哪些?
A) Curiously Recurring Template Pattern (CRTP)
B) std::enable_if
C) SFINAE
D) virtual
继承
答案:A, B, C
详解:
std::enable_if
和SFINAE用于编译期条件选择(B/C正确)。virtual
继承属于动态多态(D错误)。题目10:以下代码输出是什么?
struct Base { virtual void foo() { std::cout << "Base"; } };
struct Derived : Base { void foo() override { std::cout << "Derived"; } };
std::function<void(Base*)> func = [](Base* b){ b->foo(); };
Derived d;
func(&d);
A) Base
B) Derived
C) 编译错误
D) 未定义行为
答案:B
详解:
Base
指针指向Derived
对象,虚函数调用动态派发(B正确)。std::function
正确转发调用(无A/C/D问题)。题目1:实现一个简单的类型擦除容器
要求:设计一个类AnyCallable
,能够存储任意可调用对象(函数指针、lambda、仿函数),并提供operator()
调用。
测试用例:
AnyCallable c1 = []{ std::cout << "Lambda\n"; };
AnyCallable c2 = &foo; // 假设foo是void(*)()
AnyCallable c3 = Functor();
c1(); // 输出Lambda
c2(); // 输出Foo
c3(); // 输出Functor
答案:
class AnyCallable {
struct Concept {
virtual void call() = 0;
virtual ~Concept() {}
};
template<typename F>
struct Model : Concept {
F f;
Model(F f) : f(f) {}
void call() override { f(); }
};
std::unique_ptr<Concept> ptr;
public:
template<typename F>
AnyCallable(F f) : ptr(new Model<F>(f)) {}
void operator()() { ptr->call(); }
};
题目2:比较动态多态与静态多态的性能差异
要求:编写两个版本的计算器:
Operation
派生出Add/Sub/Mul
,通过虚函数计算。答案:
// 动态多态版
struct Operation {
virtual int calculate(int a, int b) = 0;
virtual ~Operation() {}
};
struct Add : Operation { int calculate(...) override { return a + b; } };
// Sub/Mul类似...
// 静态多态版
template<typename Op>
int calculate(Op op, int a, int b) { return op(a, b); }
struct AddOp { int operator()(int a, int b) { return a + b; } };
// SubOp/MulOp类似...
题目3:实现一个线程安全的函数注册表
要求:设计一个类FunctionRegistry
,支持注册函数名与可调用对象的映射,并发安全地通过名称调用函数。
测试用例:
FunctionRegistry reg;
reg.registerFunction("print", []{ std::cout << "Hello"; });
reg.call("print"); // 输出Hello
答案:
#include
#include
class FunctionRegistry {
std::unordered_map<std::string, std::function<void()>> map;
std::mutex mtx;
public:
void registerFunction(const std::string& name, std::function<void()> func) {
std::lock_guard<std::mutex> lock(mtx);
map[name] = std::move(func);
}
void call(const std::string& name) {
std::lock_guard<std::mutex> lock(mtx);
if (auto it = map.find(name); it != map.end()) {
it->second();
}
}
};
题目4:类型擦除的代价分析
要求:编写两个版本的向量求和:
std::function
存储求和逻辑。int
和double
向量求和,对比编译后代码大小和运行时间。答案:
// 静态版本
template<typename T>
T staticSum(const std::vector<T>& vec) {
return std::accumulate(vec.begin(), vec.end(), T{});
}
// 动态版本
std::function<double(const std::vector<double>&)> dynamicSum = [](const auto& vec){
return std::accumulate(vec.begin(), vec.end(), 0.0);
};
题目5:实现一个事件总线(Event Bus)
要求:设计一个基于类型擦除的事件总线,支持注册事件监听器和触发事件。
测试用例:
EventBus bus;
bus.subscribe<int>("number_event", [](int x){ std::cout << "Received: " << x; });
bus.publish(42); // 输出Received: 42
答案:
#include
#include
#include
class EventBus {
std::unordered_map<std::string, std::function<void(const std::any&)>> listeners;
public:
template<typename T>
void subscribe(const std::string& event, std::function<void(T)> callback) {
listeners[event] = [callback](const std::any& arg){
if (arg.type() == typeid(T)) {
callback(std::any_cast<T>(arg));
}
};
}
void publish(const std::string& event, const std::any& arg) {
if (auto it = listeners.find(event); it != listeners.end()) {
it->second(arg);
}
}
};