我编写了一个内存扫描应用程序,其中的“模式”由任意模板化的“单元”组成。单元类也派生自非模板化父包装器类(因此我可以将单元指针存储在向量中。)。
如何显示不在父类中的模板化单元成员,而无需手动向下转换?
我想做类似auto unitT = most_derived_cast(unitW);
这是示例代码:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Wrapper {
public:
virtual void* get1() const = 0;
};
template<typename T>
class Derived : public Wrapper {
T m_value;
public:
Derived(T value) : m_value{ value } {}
void* get1() const override {
return new T(m_value);
}
T get2() const {
return m_value;
}
};
int main(int argc, char** argv) {
vector<shared_ptr<Wrapper>> wrapped;
for (int i = 0; i < 10; i++) {
wrapped.emplace_back(new Derived<int>(i));
wrapped.emplace_back(new Derived<float>(i));
}
for (const shared_ptr<Wrapper>& w : wrapped) {
cout << *w->get1() << " "; // bummer; can't dereference from void*
auto v = w->get2(); // bummer; w is of type Wrapper
if (dynamic_pointer_cast<Derived<int>>(w)) { // bummer; have to manually cast
cout << *(int*)w->get1() << " ";
cout << dynamic_pointer_cast<Derived<int>>(w)->get2() << " ";
}
else if (dynamic_pointer_cast<Derived<float>>(w)) {
cout << *(float*)w->get1() << " ";
cout << dynamic_pointer_cast<Derived<float>>(w)->get2() << " ";
}
else {
cout << "bummer "; // bummer; forgot to implement this...
}
}
system("PAUSE");
return 0;
}
答案 0 :(得分:1)
C ++是否可以自动转换为大多数派生类型?
不。这不可能。通常,当人们认为自己需要此功能时,他们需要的是虚拟功能。
如何显示不包含在父类中的模板化单元成员而无需手动向下转换?
解决方案示例:
添加新的虚拟函数以插入流中:
class Wrapper {
public:
virtual std::ostream&
stream_insert(std::ostream& os) const = 0;
// ...
};
使Wrapper
可以流化,并委托给虚函数:
std::ostream&
operator<<(std::ostream& os, const Wrapper& w) {
return w.stream_insert(os);
}
在派生类中实现该功能:
std::ostream&
stream_insert(std::ostream& os) const override {
return os << m_value;
}
您现在可以将Wrapper
个实例插入字符流:
for (const shared_ptr<Wrapper>& w : wrapped) {
std::cout << *w;
}
答案 1 :(得分:1)
是否可以自动转换为大多数派生类型?
正如eeonika的回答所提到的,不,不是。
一般而言,仅给出Wrapper
的声明(甚至定义为Wrapper
),实现就没有有关从中派生出哪些类的信息。单独的编译模型(允许源文件被独立编译并链接在一起)意味着编译器不了解在不同编译单元中定义的派生类。
但是,这里的主题实际上是错误的问题。实际上,您已经提供了"XY problem"的演示,其中您尝试执行“ X”,认为“ Y”将是一个解决方案,因此您询问了如何执行“ Y”-您的案件是一个无法解决的问题,这会阻止人们为您提供帮助。
以后,在提出问题时,请尝试正确地描述您的REAL问题(“ X”),最好不要考虑您要考虑的解决方案(“ Y”)。
幸运的是,您中的少数人问如何解决“ XY问题”,并且实际上不愿意在问题的正文中包含对REAL问题的描述。这意味着可以提供帮助,我现在将尝试。
如何显示不包含在父类中的模板化单元成员而无需手动向下转换?
再次如eeroniko所述,实际的解决方案是提供合适的虚函数。
以您为例,实际的问题是您的class Wrapper {
public:
virtual void* get1() const = 0;
};
类
main()
已提供了虚函数,但是在cout << *w->get1() << " "; // bummer; can't dereference from void*
中使用该函数时,该函数没有提供足够的信息来使使用正常工作
Wrapper
要解决此问题,需要执行一些步骤。
第一步,请更改void *
,以使虚拟函数不会返回virtual Wrapper* get1() const = 0;
。
operator<<()
第二步声明一个Wrapper
,该std::ostream &operator<<(std::ostream &, const Wrapper &);
可以在不更改该对象的情况下输出到流中。
Wrapper
(可选)可以将此函数声明为const
的朋友。 &
代表正常的期望,即将对象输出到流不会更改它。通过引用传递两个参数(每个参数上的Wrapper
)很重要。
第三步提供virtual void Output(std::ostream &) const;
的另一个虚拟函数,可用于执行输出;
Derived
第四步。在模板类virtual
中,覆盖两个template<typename T> class Derived : public Wrapper
{
T m_value;
public:
Derived(T value) : m_value{ value } {}
Wrapper * get1() const override
{
return this; // note that this does not create a clone
};
virtual void Output(std::ostream &s) const override
{
s << m_value;
};
};
函数。
get1()
请注意,我已经更改了operator<<()
,所以它返回了当前对象的地址。您的实现动态创建了一个克隆,这会导致内存泄漏(除非调用者采取特定步骤来释放动态分配的对象)。
第五步定义先前声明的std::ostream &operator<<(std::ostream &s, const Wrapper &w)
{
w.Output(s);
return s;
}
,以便调用虚函数
w.Output(s)
w
实际上是对虚函数的调用。因此,如果Derived<int>
是对w.Output(s)
的引用,则Derived<int>::Output()
将正确调用Wrapper
,而无需将Derived<int>
强制转换为 cout << *w->get1() << " ";
。
将所有内容放在一起,您会发现该语句
get1()
现在有效。机制是w
返回对象的地址(即,它返回*
)。 operator<<()
获得对指向的对象的引用。然后w
调用虚函数,并解析(由于Derived<int>
指向Derived<float>
或Output()
)到Derived
的正确重载-由于它是模板类m_value
的成员,因此可以正确访问成员get2()
。
您可能希望考虑的其他优点
1)尽管我已根据需要省略了模板化Derived<T>
的{{1}}成员,但该函数也可以由Derived<T>::Output()
调用(但不能通过operator<<()
因为Wrapper
没有这样的功能,并且operator<<()
对于从Wrapper
派生出什么类一无所知。
2)尽管您的声明
cout << *w->get1() << " ";
现在可以使用,可以完全省略与get1()
相关的所有内容,只需执行
cout << *w << " ";
正确输出对象,因为*w
获得对Wrapper &
所指向的对象的引用(类型为w
)。这意味着operator<<()
将正确调用虚拟函数Output()
的最派生形式。
最后的提示:我再次强调,您无需解决您所提出的不可能的问题(如何自动将其转换为最衍生的类型)。