0

C++ 中有一个有趣的问题,但更多的是关于架构。

有许多(10、20、40 等)类描述了一些特征(混合类),例如:

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

struct A : virtual public Base { int size; };
struct B : virtual public Base { float x, y; };
struct C : virtual public Base { bool some_bool_state; };
struct D : virtual public Base { string str; }
// ....

主模块声明和导出一个函数(为简单起见,只声明没有类的函数):

// .h file
void operate(Base *pBase);

// .cpp file
void operate(Base *pBase)
{
    // ....
}

任何其他模块都可以有这样的代码:

#include "mixing.h"
#include "primary.h"

class obj1_t : public A, public C, public D {};
class obj2_t : public B, public D {};

// ...
void Pass()
{
    obj1_t obj1;
    obj2_t obj2;

    operate(&obj1);
    operate(&obj2);
}

问题是你怎么知道给定对象的真实类型是什么operate()而不使用dynamic_cast类中的任何类型信息(常量等)?该operate()函数在小时间段内与大量对象一起使用,并且dynamic_cast速度太慢,我不想包含常量(enum obj_type { ... }),因为这不是 OOP 方式。

// module operate.cpp

void some_operate(Base *pBase)
{
    processA(pBase);
    processB(pBase);
}

void processA(A *pA)
{
}

void processB(B *pB)
{
}

我不能直接将 a 传递pBase给这些函数。而且不可能有所有可能的类组合,因为我可以通过包含新的头文件来添加新的类。

想到的一种解决方案是,在编辑器中我可以使用复合容器:

struct CompositeObject
{
    vector<Base *pBase> parts;
};

但是编辑器不需要时间优化,可以dynamic_cast根据零件来确定准确的类型。在operate()我不能使用这个解决方案。

那么,是否可以避免使用adynamic_cast和type信息来解决这个问题呢?或者也许我应该使用另一种架构?

4

4 回答 4

2

首先想到的是询问您真正想要实现的目标......但第二个想法是您可以使用访问者模式。运行时类型信息将隐式用于确定层次结构中的哪个点是 accept 方法的最终覆盖器,但您不会显式使用该信息(您的代码不会显示任何dynamic_cast, type_info, 常量...)

话又说回来,我的第一个想法又回来了……既然您在问架构的适当性,那么您真正想要实现的是什么?——如果不了解问题,你只会找到像这个一样的通用答案。

于 2010-04-08T11:01:22.763 回答
2

这里真正的问题是关于你想要达到的目标。

你想要类似的东西:

void operate(A-B& ) { operateA(); operateB(); }

// OR

void operate(A-B& ) { operateAB(); }

也就是说,您是想对每个子组件(独立地)应用操作,还是希望能够根据组件的组合应用操作(更难)。

我将在这里采取第一种方法。

1.虚拟?

class Base { public: virtual void operate() = 0; };

class A: virtual public Base { public virtual void operate() = 0; };
void A::operate() { ++size; } // yes, it's possible to define a pure virtual

class obj1_t: public A, public B
{
public:
  virtual void operate() { A::operate(); B::operate(); }
};

当然还有一些工作。值得注意的是,我不太喜欢重复。但这是对 _vtable 的一次调用,因此它应该是最快的解决方案之一!

2.复合模式

这可能是这里更自然的事情。

请注意,您可以在 C++ 中完美地使用该模式的模板版本!

template <class T1, class T2, class T3>
class BaseT: public Base, private T1, private T2, private T3
{
public:
  void operate() { T1::operate(); T2::operate(); T3::operate(); }
};

class obj1_t: public BaseT<A,B,C> {};

好处:

  • 不再需要重复自己!operate一劳永逸地写(裸变量...)
  • 只有1个虚拟调用,没有更多的虚拟继承,比以前更高效
  • AB并且C可以是任意类型,它们根本不应该继承Base
  • 编辑, 的operate方法AB现在C可以内联,因为它不是virtual

坏处:

如果您还没有访问可变参数模板的权限,请在框架上进行更多工作,但在几十行内它是可行的。

于 2010-04-08T12:07:17.627 回答
1

通常的面向对象方法是在基类中具有(纯)虚函数,这些虚函数被调用operate()并在派生类中被覆盖以执行特定于该派生类的代码。

于 2010-04-08T11:01:39.547 回答
1

您的问题是您想根据多个对象的类型来决定要做什么。虚函数仅对一个对象(.or的左侧->)执行此操作。对多个对象执行此操作称为多重分派(对于两个对象也称为双重分派),而在 C++ 中没有内置功能可以处理此问题。

查看双重调度,尤其是在访问者模式中。

于 2010-04-08T11:09:24.803 回答