我想在C++中使用多态性,我试图将所有派生类中的方法显示提取到基类中。
例如:
我有两个类,HouseA
和HouseB
,它们是模板类。它们是从基类BaseHouse
派生的。
class BaseHouse
{
public:
//other thing
private:
};
template <typename Type>
class HouseA : public BaseHouse
{
public:
HouseA(Type object_input) : object(object_input)
{
}
// other thing about HouseA
Type &getObject()
{
std::cout << "this is House A" << std::endl;
return object;
}
private:
Type object;
};
template <typename Type>
class HouseB : public BaseHouse
{
public:
HouseB(Type object_input) : object(object_input)
{
}
// other thing about HouseB
Type &getObject()
{
std::cout << "this is House B" << std::endl;
return object;
}
private:
Type object;
};
由于多态性,我们使用基类的指针来访问派生的类对象。当我需要调用在派生类中定义的方法时,我总是将基类指针转换为派生类指针:
int main()
{
HouseA<int> house_a(5);
int x = house_a.getObject();
BaseHouse *base_ptr = &house_a;
// suppose after some complicate calculate calculation
// we only have the base class pointer can access derivated class object
HouseA<int> *ptr_a = (HouseA<int> *)base_ptr; //transfer base class pointer into derivated class pointer
ptr_a->getObject();
return 0;
}
但是派生类HouseA
和HouseB
都有getObject
方法。
所以我想把模板派生类的方法提取到非模板基类中。
由于某种原因,我们假设基类BaseHouse
不能是模板类。
我有没有办法做到这一点?
提前谢谢。
发布于 2019-05-25 23:35:56
如果派生成员的签名依赖于模板参数(就像getObject对类型所做的那样),则不能将该成员提取到非模板库中。至少在不删除成员签名根据模板参数而变化的能力的情况下是这样的。
发布于 2019-05-28 05:19:21
也许不是一个典型的访客但是..。
好的,基本的想法是,我们必须以某种方式捕获模板化处理,并将其封装到一个运行时多态构造中随时可用的单个实体中。
让我们从一个简单的类层次结构开始:
struct Consumer;
struct Base {
virtual void giveObject(Consumer const &) const = 0;
virtual ~Base() = default;
};
struct Derived1: Base {
Derived1(int x): x(x) {}
void giveObject(Consumer const &c) const override {
c(x);
}
private:
int x;
};
struct Derived2: Base {
Derived2(double y): y(y) {}
void giveObject(Consumer const &c) const override {
c(y);
}
private:
double y;
};
到目前为止,它非常简单:Base
类有一个接受Consumer
类型对象的纯虚方法,该方法的一个具体实现应该向Consumer
公开其特定实现者(这是Base
的一个子类型)的内部状态的相关部分。换句话说,我们采用了“虚拟模板”的习惯用法,并将其隐藏在Consumer
中。好吧,那会是什么呢?
第一种选择,如果你在编译时(更准确地说,在源代码时)预先知道它可能做什么,即每个对象类型只有一个消耗算法,并且类型集是固定的,那么它就非常简单:
struct Consumer {
void consume(int x) const { std::cout << x << " is an int.\n"; }
void consume(double y) const { std::cout << y << " is a double.\n"; }
template<typename T> void consume(T t) const {
std::cout << "Default implementation called for an unknown type.\n";
}
};
等。
更复杂的实现将允许运行时构造模板化的实体。这怎么可能呢?
Alexandrescu在他的“现代C++设计”中使用typeid
在单个数据结构中存储特定类型的处理程序。简而言之,这可能是这样的:
struct Handler {
virtual ~Handler() = default; // now it's an empty polymorphic base
};
template<typename T> struct RealHandler: Handler {
RealHandler(std::function<void(T)> f): f(std::move(f)) {}
void handle(T x) {
f(x);
}
private:
std::function<void(T)> f;
};
#include <map>
#include <type_info>
#include <functional>
struct Consumer {
template<typename T> void consume(T t) const {
auto f{knownHandlers.find(typeid(t))};
if(f != knownHandlers.end()) {
RealHandler<T> const &rh{
dynamic_cast<RealHandler<T> const &>(*f->second)};
rh.handle(t);
}
else {
// default implementation for unregistered types here
}
}
template<typename T> Consumer ®ister(std::function<void(T)> f) {
knownHandlers[typeid(T)] = std::make_unique<RealHandler<T>>(std::move(f));
}
private:
std::map<std::type_info, std::unique_ptr<Handler>> knownHandlers;
};
我还没有真正测试过它,因为我不太喜欢typeids和其他RTTI。我很快测试了另一个解决方案,它既不需要map也不需要typeinfo来以模板化的方式存储处理程序。尽管如此,它仍然使用了一个小技巧,比如我们如何通过相同的调用传递、保存和检索任意类型的信息。
struct Consumer {
Consumer() {}
template<typename T> void consume(T t) const {
auto f{setSlot<T>()};
if(f) f(t);
else {
// default implementation for an unset slot
std::cout << t / 2 << '\n';
}
}
template<typename T>
std::function<void(T)> &setSlot(
std::function<void(T)> f = std::function<void(T)>{}) const
{
static std::function<void(T)> slot;
if(f) { // setter
slot = std::move(f);
}
return slot;
}
};
在这里,setSlot()
用于存储特定类型的处理程序:当使用非空参数调用时,它存储该参数;然后返回当前保留的值。有了这样定义的Consumer
,上面的类层次结构的工作方式如下:
int main() {
Consumer c;
c.setSlot<int>([](int x){ std::cout << x << " is an int!\n"; });
Base const &b1{Derived1{42}};
Base const &b2{Derived2{3.14}};
b1.giveObject(c);
b2.giveObject(c);
}
输出:
42 is an int!
1.57
在第一行中,我们看到一条由自定义int
处理程序打印的消息;在第二行中,打印了一条double
类型的默认消息,因为没有安装double
的自定义处理程序。
这种实现的一个明显缺点是处理程序存储在static
变量中,因此所有的Consumer
对于所有类型共享相同的处理程序,所以这里的Consumer
实际上是单状态的。至少,您可以在运行时更改类型的实现,这与修复第一种方法的Consumers
不同。上面的maps of-typeids方法不应该有这个缺点,但要付出一些性能代价。
https://stackoverflow.com/questions/56306180
复制相似问题