c++ - 可编程访问正确的虚拟班级孩子?
问题描述
我有一个虚拟/父类和这个类的许多孩子。我假设这些孩子是用一种 API 生成问题答案的不同方式——例如各种通信协议。他们可以有不同的版本。
class A {
public: virtual const char * [] GetProtocolName () {return "A"; }
};
class B: public A {
public: virtual const char * [] GetProtocolName () {return "B"; }
};
class C: public B {
public: virtual const char * [] GetProtocolName () {return "C"; }
};
....
假设在程序中我要列出我的子类/子列表——每个类都有一个函数:* char [] GetProtocolName()
并基于协议:协议 1
协议 2
协议 x...
用户可以选择应由哪个类处理通信
我的问题如下:
如何在程序中 - 即编译后,以及如何将其保存在代码中,在编译之前 - 我可以确定所选类将是我的虚拟类/父级的子 X - 基于文本设置(SELECT USER in this列出类)。
问题是两件事:如何将每个可用类列为 A 类的子类,这些类在程序中可用
如何分配一个孩子 - 根据您从列表中选择的内容(即基于 * char [])从众多协议中选择一个协议?
class * communicationProtocol = ?????
我是这个主题的新手。谢谢你的任何提示。我不知道该使用什么短语,而我想要的短语给了我我已经拥有的知识。
解决方案
一种常见的方法是使用“注册表”。您根据名称定义一个映射(您可以向用户询问或从文件中读取)和一个创建适当子项的函数。
以下是一个玩具但完整的示例;请注意如何main
不需要知道所有可能的派生类的列表,并且仍然可以从其名称创建给定派生类类型的对象:
#include <iostream>
#include <map>
#include <string>
struct Shape {
virtual void draw() = 0;
virtual ~Shape() {};
};
struct Triangle : Shape {
virtual void draw() override { std::cout << "I'm a triangle!\n"; }
};
struct Circle : Shape {
virtual void draw() override { std::cout << "I'm a circle!\n"; }
};
std::map<std::string, Shape *(*)()> registry{
{"triangle", []()->Shape* { return new Triangle; }},
{"circle", []()->Shape* { return new Circle; }}
};
int main(int argc, const char *argv[]) {
if (argc != 2) {
std::cout << "You need to choose the shape type:\n";
for (auto& i : registry) {
std::cout << " " << i.first << "\n";
}
} else {
auto i = registry.find(argv[1]);
if (i == registry.end()) {
std::cout << "Unknown shape type\n";
} else {
Shape *s = i->second();
s->draw();
delete s;
}
}
return 0;
}
您应该注意的一个微妙细节是,如果派生类是在其他编译单元中定义的,并且它们的注册也在这些单元的静态初始化期间完成,则形式上存在链接器根本不会考虑这些类的风险,除非有对单位的一些明确引用。换句话说,假设你有:
/// File a.cpp
#include "base.h"
#include "registry.h"
struct DerivedA : Base {
...
};
static int init_a = [](){
registry["a"] = []()->Base* { return new DerivedA; };
return 1;
}();
以及所有其他派生类的类似文件。
然后 C++ 标准说,即使您在程序中编译并链接它们,这些类也可能不会被注册。
发生这种情况是因为init_a
初始化可能会延迟到第一次访问编译单元中的任何对象完成。但是如果init_a
没有初始化,那么就没有人可以访问这个类,因为这个名字不会在注册表中。不能保证静态持续时间对象的动态初始化在 开始之前执行main
,您只能保证它在该编译单元中使用任何符号之前执行。
不幸的是,C++ 中没有可移植性atstart()
。
换句话说,使用自注册模块(即在程序中的其他任何地方都没有引用这些编译单元中的任何符号)不是可移植的 C++。它可能有效,但没有这样的保证。
为了安全起见,您应该只显式注册所有类。
推荐阅读
- php - 如何将值从存储库传递到 laravel 中的控制器
- laravel - Laravel Blade 中的“::”语法是什么意思(例如:@include('googletagmanager::head'))?
- javascript - 如何在 JSON 数据中搜索动态结果
- django - Django 日志配置在本地服务器上工作,但不在远程服务器上
- node.js - 这个问题 TypeError: callback is not a function
- excel - MS Excel:将日期标准化为一种格式
- react-native - 向上滚动 FlatList 反应原生时如何获取更多数据?
- haskell - Monad中的do符号在Haskell中是什么意思
- swiftui - SwiftUI:未应用对齐
- android - leakcanary FirestoreRecyclerAdapter 内存泄漏