如何解决自动将父类中的 shared_ptr 向下转换为子类类型
我有一个虚拟父类,用于收集带有相关报告结构的报告。报告最终应呈现为 JSON 字符串,因此我使用 https://github.com/nlohmann/json 来帮助我。
我创建了不同的不同子类来生成相应子报表结构的此类报表,挑战在于每个子报表可能具有略有不同的字段,但会从父类继承一些字段。我有将结构转换为 JSON 表示所需的宏,按报告类型定义。这是到目前为止的代码:
/**
* Compile with nlohmann json.hpp
*/
#include <iostream>
#include <vector>
#include <memory>
#include "json.hpp"
using json = nlohmann::json;
struct Report {
// make abstract
virtual ~Report() {}
std::string type = "main_report";
int foo = 0;
};
struct ChildAReport : public Report {
std::string type = "child_a_report";
int bar = 1;
};
struct ChildBReport : public Report {
std::string type = "child_b_report";
int baz = 2;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ChildAReport,type,foo,bar)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ChildBReport,baz)
class Parent {
protected:
std::vector<std::shared_ptr<Report>> reports;
virtual void run() = 0;
virtual std::string get_report() = 0;
};
class ChildA : public Parent {
public:
virtual void run() override
{
ChildAReport r;
r.foo = 1;
r.bar = 2;
reports.push_back(std::make_shared<ChildAReport>(r));
}
std::string get_report() override {
std::shared_ptr<Report> r = reports.back();
std::shared_ptr<ChildAReport> cr = std::dynamic_pointer_cast<ChildAReport>(r);
json r_json = *cr;
return r_json.dump();
}
};
class ChildB : public Parent {
public:
virtual void run() override
{
ChildBReport r;
r.foo = 1;
r.baz = 3;
reports.push_back(std::make_shared<ChildBReport>(r));
}
std::string get_report() override {
std::shared_ptr<Report> r = reports.back();
std::shared_ptr<ChildBReport> cr = std::dynamic_pointer_cast<ChildBReport>(r);
json r_json = *cr;
return r_json.dump();
}
};
int main(int argc,char *argv[])
{
ChildA ca = ChildA();
ca.run();
std::cout << ca.get_report() << std::endl;
ChildB cb = ChildB();
cb.run();
std::cout << cb.get_report() << std::endl;
}
代码是用 json.hpp
编译的,没有进一步的依赖。
我期待这个输出:
{"bar":2,"foo":1,"type":"child_a_report"}
{"baz":3,"type":"child_b_report"}
现在,为了实际生成 JSON,我使用了 get_report()
方法。我了解到我必须将 downcast the pointer 到 Report
结构到实际的 ChildA
或 ChildB
结构,否则它将无法正确转换为 JSON。这很乏味;如您所见,代码在每个可能的子类中几乎是逐字重复的。 run()
函数不是问题 - 这里是各种魔法发生的地方,每个类都不同。
有没有一种方法可以将其拉到父类中,而无需在转换为 JSON 之前明确指定要进行的类型转换?理想情况下,这可以根据运行 get_report()
的实际类型来推断......
解决方法
还有一个想法是让父类成为模板类,它使用子报表类型作为模板参数:
#include <iostream>
#include <vector>
#include <memory>
struct Report {
// make abstract
virtual ~Report() {}
std::string type = "main_report";
int foo = 0;
};
struct ChildAReport : public Report {
std::string type = "child_a_report";
int bar = 1;
};
struct ChildBReport : public Report {
std::string type = "child_b_report";
int baz = 2;
};
template<typename REPORT>
class Parent
{
public:
std::string get_report()
{
auto r = reports.back();
return r->type;
}
virtual void run() = 0;
protected:
std::vector<std::shared_ptr<REPORT>> reports;
};
class ChildA : public Parent<ChildAReport> {
public:
virtual void run() override
{
ChildAReport r;
r.foo = 1;
r.bar = 2;
reports.push_back(std::make_shared<ChildAReport>(r));
}
};
class ChildB : public Parent<ChildBReport> {
public:
virtual void run() override
{
ChildBReport r;
r.foo = 1;
r.baz = 3;
reports.push_back(std::make_shared<ChildBReport>(r));
}
};
int main()
{
auto ca = ChildA();
ca.run();
std::cout << ca.get_report() << std::endl;
auto cb = ChildB();
cb.run();
std::cout << cb.get_report() << std::endl;
return 0;
}
编辑答案,因为不再需要纯虚方法
更新您的评论:
为要在示例中使用的父类创建一个基类(接口):
class IGrandparent
{
public:
virtual std::string get_report() = 0;
virtual void run() = 0;
};
template<typename REPORT>
class Parent : public IGrandparent
{
public:
std::string get_report() override
{
auto r = reports.back();
return r->type;
}
protected:
std::vector<std::shared_ptr<REPORT>> reports;
};
int main()
{
std::unique_ptr<IGrandparent> childAInDisguise = std::make_unique<ChildA>();
std::unique_ptr<IGrandparent> childBInDisguise = std::make_unique<ChildB>();
childAInDisguise->run();
std::cout << childAInDisguise->get_report() << std::endl;
childBInDisguise->run();
std::cout << childBInDisguise->get_report() << std::endl;
return 0;
}
,
您可以做的一件事是在基类 Parent 中有一个模板,但这意味着删除 virtual 关键字,因此 Parent 类将不再是接口。这只是使 get_report 不那么乏味的简单方法,如果 Parent 类必须是接口,则此解决方案无效。
如果这对你有帮助,它会是这样的:
class Parent {
protected:
std::vector<std::shared_ptr<Report>> reports;
virtual void run() = 0;
template <class T>
std::string get_report_base()
{
std::shared_ptr<Report> r = reports.back();
std::shared_ptr<T> cr = std::dynamic_pointer_cast<T>(r);
json r_json = *cr;
return r_json.dump();
}
};
孩子们会调用 get_report_base:
class ChildA : public Parent {
public:
virtual void run() override
{
ChildAReport r;
r.foo = 1;
r.bar = 2;
reports.push_back(std::make_shared<ChildAReport>(r));
}
std::string get_report() {
return get_report_base<ChildAReport>();
}
};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。