gpt4 book ai didi

c++ - 如何为任意(但在编译时定义)功能支持定义抽象基类?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:20:20 26 4
gpt4 key购买 nike

我有以下C++设计问题,非常感谢任何建议/解决方案。
请注意,我的背景不是计算机科学,因此我可能缺少一些明显的解决方案。

我通常在代码中分离关键组件的方式是通过抽象类和纯虚函数定义接口(interface)。

范例1:

class B
{
public:
virtual double f( double x ) = 0;
};

class D1 : public B
{
public:
double f( double x ) const
{
return 0.0;
}
};

class D2 : public B
{
public:
double f( double x ) const
{
return 1.0;
}
};

这样我就可以很好地将接口(interface)与实现分开。
这种方法也非常快(而且由于我正在研究的是数字库,所以这很重要:P)。

现在,我面临的问题是以下问题。

我有一组“功能”,可以通过功能(在下面定义)f(),g()和h()进行总结。
请注意,所有这些函数的参数和返回类型通常都不同。

假设我有一些代码需要一个指向实现功能f()和g()的对象的指针。
我想做的是能够传递具有“更多或相等”功能的东西,例如支持f(),g()和h()的东西。

为了更好地解释自己,这里是一些代码。
请注意,我可以使用“嵌套初始化”方法来代替多重继承,例如在boost::operators中。这里的要点是,我永远不会遇到f()与g()相同的情况。所有功能都不同。
问题是,为了完成这项工作,我需要使用reinterpret_cast,如下面的示例所示(因此,这并不是真正的解决方案):

范例2:
class F {
public:
virtual double f( double x ) = 0;
};

class G {
public:
virtual double g( double x ) = 0;
};

class H {
public:
virtual double h( double x ) = 0;
};

class N {};

template<class T1, class T2=N, class T3=N>
class Feature : public T1 , public T2 , public T3
{
};

template<class T1, class T2>
class Feature<T1,T2,N> : public T1, public T2
{
};

template<class T1>
class Feature<T1,N,N> : public T1
{
};

//Supp for Supports/Implements
class SuppFandG : public Feature<F,G>
{
public:
double f( double x ) { return 0.0; }
double g( double x ) { return 1.0; }
};

class SuppFandH : public Feature<F,H>
{
public:
double f( double x ) { return 0.0; }
double h( double x ) { return 1.0; }
};

class SuppFandGandH : public Feature<F,G,H>
{
public:
double f( double x ) { return 0.0; }
double g( double x ) { return 1.0; }
double h( double x ) { return 2.0; }
};

int main()
{
Feature<F,G>* featureFandGPtr;
Feature<F,H>* featureFandHPtr;
Feature<H,F>* featureHandFPtr;
Feature<F,G,H>* featureFandGandHPtr;

SuppFandGandH suppFandGandH;
featureFandGandHPtr = &suppFandGandH;

//featureFandGPtr = featureFandGandHPtr; //Illegal. static_cast illegal too.
//the reason to do this is that I would like to pass a pointer to an object
//of type Feature<F,G,H> to a function (or constructor) that expects a pointer to Feature<F,G>
featureFandGPtr = reinterpret_cast< Feature<F,G>* >( featureFandGandHPtr );
featureFandHPtr = reinterpret_cast< Feature<F,H>* >( featureFandGandHPtr );
featureHandFPtr = reinterpret_cast< Feature<H,F>* >( featureFandGandHPtr );

featureFandGPtr->f( 1.0 );
featureFandGandHPtr->h( 1.0 );
}

或者,我可以尝试构造继承层次结构,但更改Feature的定义,但是以下示例使Visual Studio 2008专业版编译器崩溃,因此无法对其进行测试。

范例3:
//This will not work, Visual studio 2008 professional crash.
template<class T1, class T2=N, class T3=N>
class Feature : public Feature<T1,T2> , public Feature<T1,T3> , public Feature<T2,T3>
{
};

template<class T1, class T2>
class Feature<T1,T2,N> : public Feature<T1>, public Feature<T2>
{
};

template<class T1>
class Feature<T1,N,N> : public T1
{
};

用这种方法我仍然有问题
1)Feature在功能上(对于我想要实现的功能)等效于Feature,但是它们的类型不同。
但是,这可以通过使用MPL boost库(总是对类型进行“排序”)进行一些花哨的元编程来解决,因此为简单起见,我们假设这不是问题。

2)存在多个基础的问题,我想避免通过虚拟基础进行虚拟继承(性能下降)。
通过使用Feature专长中的指令,可以解决此问题。

不过,我还是不能100%地确定是否可以进行这项工作,因此无法针对大量功能进行很好的扩展。
实际上,构成层次结构的元素数量由二项式系数(几乎是阶乘)给出:
F-> F(1)
F,G-> FG,F,G(3)
F,G,H-> FGH,FG,GH,FH,F,G,H(7)

我想知道是否存在涉及以下条件的设计问题的解决方案:

1)代码应具有与示例1相同的运行时性能。

2)我希望能够轻松地指定一些功能集,并能够“传递”指向具有此(通常是额外的)功能的对象的指针。

3)每当我在别处考虑新功能h()时,我都希望依赖功能f()和g()的代码不需要重新编译。

4)我不想模板化所有想要使用这些功能的东西(几乎所有代码)。应该有某种“分离”,请参见第3点。

在(数字)库中查看时,通常会发现两种方法:

1)定义一个巨大的抽象基类B,它具有f(),g(),h(),......
问题:每当我想添加新功能z()时,都必须修改B,所有内容都需要重新编译(即使此代码根本不关心z()),所有现有的实现D1,D2 B的,...需要修改(通常是通过让它们分别抛出z()的异常,以支持z()的新实现)。
当我需要添加特征时,逐步放大B的解决方案并不是解决当前问题的好方法,因为特征f()和g()实际上与h()和i()一样“重要”,两者都不比其他人“更基础”。

2)分离所有功能,并为每个功能使用一个指针。
但是,这对于用户来说很麻烦(在大多数情况下,必须携带4个或更多的指针),而且对于手头的问题,这种方法不是最佳的(这里实际上是1个对象可能会或可能不会做某事,实际上,调用f()会修改g()获得的结果,反之亦然。

预先感谢您的帮助。

克劳

最佳答案

现在,这是一个有趣的问题!

显而易见的(简单)解决方案是使模板疯狂。如果每个功能仅需要一个接口(interface)(而不是一个确定的类型),则可以减轻您的大部分顾虑。但是,当然这在依赖方面有其自身的惩罚,您在第4点中拒绝了该解决方案。

动态转换?

struct Feature { virtual ~Feature() {} };

class F: public Feature {};
class G: public Feature {};

现在,这样声明一个实现 FG的类
class Impl: public F, public G {};

像这样需要F和G的方法
void method(Feature const& i)
{
F const& myF = dynamic_cast<F const&>(i);
G const& myG = dynamic_cast<G const&>(i);

myF.f(2.0);
myG.g(2.0);
}

诚然,可能会有一点性能损失,并且在编译时不会检查类型安全性。

模板转发

但是,这需要另一种解决方案:
namespace detail
{
void methodImpl(F const& f, G const& g);
}

template <class T>
void method(T const& t)
{
detail::methodImpl(t,t);
}

这将 BostonLogan的方法与更好的接口(interface)结合在一起。没有用户代码应该提及 detail命名空间(易于测试),如果这样,则可以确保没有人使用两个不同的对象来调用 methodImpl

这似乎可以满足您的大多数需求:
  • 运行时性能等效于您的示例
  • T可能是从HZ继承的,您不在乎,只要它从FG继承,则代码将编译
  • 添加其他功能不会更改任何内容
  • ...好,您必须对接口(interface)进行模板化(method),但这是非模板方法的单线转发器,该方法可以进行实际的繁重工作。

  • 唯一令我烦恼的是 methodImpl实际上有2个对同一对象的引用,这可能会给将来带来麻烦。

    摆脱多个引用

    这并非易事,但我们将包装此对象并委派工作。

    这个想法是,从继承自给定功能集的未知类型的对象中,我们可以创建一个已知类型的对象,该对象将所有操作转发到第一个对象(它在其构造函数中接受)。

    为了实现这一目标,我们还需要两件事:
  • 每个Feature应该声明一个转发器
  • 我们需要一个转发器聚合系统

  • 让我们涵盖第一点:
    class F
    {
    public:
    void f(double d);
    void f2(double d, double e) const;
    };

    class FForwarder
    {
    public:
    FForwarder(F& f) : m_object(f) {}

    void f(double d) { m_object.f(d); }
    void f2(double d, double e) const { m_object.f(d,e); }

    private:
    F& m_object;
    };

    很容易,但是很麻烦。

    让我们介绍一下聚合:
    struct nil {};

    template <class Head, class Tail>
    struct Aggregator: Head, Aggregator<Tail::head, Tail::tail>
    {
    typedef Head head;
    typedef Tail tail;

    template <class T>
    Aggregator(T& t) : Head(t), Aggregator<Tail::head, Tail::tail>(t) {}
    };

    template <class Head>
    struct Aggregator<Head,nil> : Head
    {
    typedef Head head;
    typedef nil tail;

    template <class T>
    Aggregator(T& t) : Head(t) {}
    };

    template <>
    struct Aggregator<nil,nil>
    {
    typedef nil head;
    typedef nil tail;

    template <class T>
    Aggregator(T&) {}
    };

    现在,继续使用。

    通过给调用者增加负担:
    int method(Aggregator<FForwarder, Aggregator<GForwarder, HForwarder> >& fgh);

    或通过编写模板转发器:
    namespace detail
    {
    typedef Aggregator<FForwarder, Aggregator<GForwarder, HForwarder> > methodImplArg;
    int methodImpl(methodImplArg& arg);
    }

    template <class T>
    int method(T& t)
    {
    detail::methodImplArg arg = detail::methodImplArg(t);
    //named temporary because it is passed by reference to non-const

    return detail::methodImpl(arg);
    // forward the result as well
    };

    这解决了 methodImpl相当整齐地传递2个参数的问题,尽管它确实需要额外的工作...我想应该有一个更简单的方法,但还不能查明。

    关于c++ - 如何为任意(但在编译时定义)功能支持定义抽象基类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1723796/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com