C++ 将两种类型组合在一起

C++ 将两种类型组合在一起,c++,c++11,gcc,C++,C++11,Gcc,我使用我无法控制的第三方库。它包含两个类A和B,它们都定义了一个同名的方法: class A { public: ... void my_method (); }; class B { public: ... void my_method (); }; 我想创建一个C类,其中包含一个属于a类或B类的成员。至关重要的是,我只能在运行时知道我是否需要a或B类。这个C类只会调用方法my_method 如果我可以修改代码

我使用我无法控制的第三方库。它包含两个类A和B,它们都定义了一个同名的方法:

class A {
    public:
        ...
        void my_method ();
};

class B {
    public:
        ...
        void my_method ();
};
我想创建一个C类,其中包含一个属于a类或B类的成员。至关重要的是,我只能在运行时知道我是否需要a或B类。这个C类只会调用方法
my_method

如果我可以修改代码,我只需使A和B从定义我的_方法的父类(接口)派生。但我不能

创建此类C的最简单/最优雅的方法是什么?我当然可以这样定义C:

class C {
    public:
         void call_my_method() { if (a) a->my_method() else b->my_method(); }
    private:
         A* a;
         B* b;

但我想避免每次都要为if语句付出代价。它也感觉不雅。有没有一种方法可以创建a类或B类的超级类型?或者这个问题的任何其他解决方案?

否;在某个时候,您需要分支。您所能做的最好的事情是将分支向上/向下提升到调用堆栈†,这样更多的程序被封装在具象的
if
/
else
构造中,并且分支本身需要执行的频率较低。当然,您需要复制更多的程序源代码,这并不理想

目前我建议的唯一改进是一种结构,如
boost::variant
。它基本上完成了您已经在做的事情,但占用的内存更少,并且没有间接层(使用所谓的标记联合)。它仍然需要在访问上进行分支,但在分析显示这是一个很大的瓶颈之前(您可能会发现分支预测在很大程度上缓解了这种风险),我不会对您的更改做进一步的修改。&ddagger

†我永远记不起它是怎么走的,哈哈


&达格尔;一个这样的更改可能是有条件地初始化函数指针(或modern
std::function
),然后每次调用该函数。然而,这是很多间接的。您应该配置文件,但我希望它在缓存上会更慢、更硬。OO纯粹主义者可能会推荐多态继承树和虚拟分派,但一旦您如此关注性能,这对您就没有任何用处。

您可以使用
std::function
(但不确定它是否有更好的性能),例如:

class C {
    public:
         void call_my_method() { my_method(); }

         void use_a(A* a) { my_method = [=]() { a->my_method() }; }
         void use_b(B* b) { my_method = [=]() { b->my_method() }; }
    private:
         std::function<void()> my_method;
};
C类{
公众:
void调用我的方法(){my_方法();}
void使用_a(a*a){my_method=[=](){a->my_method()};}
void使用_b(b*b){my_method=[=](){b->my_method()};}
私人:
std::函数my_方法;
};

使用“基类”(C)将继承与虚拟函数一起使用如何:

那么这就行了:

C * d = new D();
d->do_method();

建议将A和B对象包装到某个助手模板TProxy中,该模板实现了IProxy接口。类C(或使用者)将使用IProxy接口,并且不知道代理中对象的类型

#include <stdio.h>

struct A {
        void func () { printf("A::func\n"); }
};

struct B {
        void func () { printf("B::func\n"); }
};

struct IProxy
{
    virtual void doFunc() = 0;
    virtual ~IProxy() {};
};

template<typename T>
struct TProxy : public IProxy
{
    TProxy(T& i_obj) : m_obj(i_obj) { }

    virtual void doFunc() override { m_obj.func(); }

private:
    T& m_obj;
};

class Consumer
{
public:
    Consumer(IProxy& i_proxy) : m_proxy(i_proxy) {}

    void Func() { m_proxy.doFunc();}

private:
    IProxy& m_proxy;
};

可能使用模板参数作为基类?@MattiaF。问题是“我只能在运行时知道我是否需要A或B”模板不起作用,因为我只能在运行时知道我是否需要调用A或B。将函数指针存储在C中?用另一个类包装A和B怎么样?我怀疑函数调用会比可预测的分支更快?不过这只是个疑问。也许这会更快。它需要分析。我想它在流水线架构中会更快,现在到处都是。现在它是一个函数调用和虚拟表查找。@LightnessRacesinOrbit没有免费的抽象。@n.m.我意识到了这一点,但这并不意味着你必须选择你能找到的最昂贵的:)@LightnessRacesinOrbit谁在乎,我够得着;)除非对应用程序的性能有可测量的影响,否则请选择最具表现力的解决方案。@n.m.:是的,并且
boost::variant
用法非常危险,表现力:)
#include <stdio.h>

struct A {
        void func () { printf("A::func\n"); }
};

struct B {
        void func () { printf("B::func\n"); }
};

struct IProxy
{
    virtual void doFunc() = 0;
    virtual ~IProxy() {};
};

template<typename T>
struct TProxy : public IProxy
{
    TProxy(T& i_obj) : m_obj(i_obj) { }

    virtual void doFunc() override { m_obj.func(); }

private:
    T& m_obj;
};

class Consumer
{
public:
    Consumer(IProxy& i_proxy) : m_proxy(i_proxy) {}

    void Func() { m_proxy.doFunc();}

private:
    IProxy& m_proxy;
};
int main()
{
    A a;
    TProxy<A> aProxy(a);

    B b;
    TProxy<B> bProxy(b);

    Consumer consumerA{aProxy};
    consumerA.Func();

    Consumer consumerB{bProxy};
    consumerB.Func();

    return 0;
}
A::func
B::func