扩展C ++类/结构的数据成员的更好方法

时间:2012-04-07 14:01:00

标签: c++ oop class containers

我一次又一次地遇到这个问题......但仍然没有一个令人满意的答案......

特别是当我将类放入容器中时,稍后我需要在特定处理期间记录容器中每个元素的更多信息,但是在处理之后我不再需要额外的信息....

我经常发现一些库试图通过在其数据结构中定义void *来解决上述情况,以提供用户定义的数据结构扩展。与此Q&A中描述的相同。 但它会产生内存/资源处理问题......以及其他我认为这种方法容易出错的问题。

在面向对象编程的现代,我在思考 使用继承&多态性。在容器中使用基类的指针,但是我必须将派生类的访问器添加到基类中。这有点奇怪......

还有其他更好的方法来扩展类的属性,同时在C ++中保持容器的可比性吗?

6 个答案:

答案 0 :(得分:3)

存储关于对象的额外数据而不实际损害对象本身完整性的最佳方法是将一对数据存储在容器中。

struct User { ... };
struct ExtraData { ... };
typedef std::pair<User, ExtraData> UserAndExtraData;

现在我可以在C ++中创建一个容器类型,它将两个信息存储在一起,而不会影响任何一种类型的独立性。

std::vector<UserAndExtraData> vector;

答案 1 :(得分:2)

我会调查Decorator Pattern。您可以在处理它们时装饰对象,然后将装饰的物体扔掉。如果有大量共享数据,您还可以查看FlyWeight pattern

答案 2 :(得分:0)

如果你的对象在一个向量中,那么一个简单的方法是制作一个平行向量:

void doSomething(const vector<MyObject>& my_objects)
{
  vector<ExtraData> extra_data;
  int n_objects = extra_data.size();
  extra_data.reserve(n_objects);
  for (int i=0; i!=n_objects; ++i) {
    extra_data.push_back(calcExtraData(my_objects[i]));
  }
  // now use my_objects[i] and extra_data[i] together.
  // extra data goes away when the function returns.
}

您不必修改原始对象,效率非常高。

如果您有其他容器类型,则可以使用地图:

void doSomething(const set<MyObject>& my_objects)
{
  map<MyObject*,ExtraData> extra_data;
  set<MyObject>::const_iterator i=my_objects.begin(), end=my_objects.end();
  for (;i!=end;++i) {
    extra_data[&*i] = calcExtraData(*i);
  }
  // now use extra_data[&obj] to access the extra data for obj.
  // extra data goes away when the function returns.
}

这不如向量那么有效,但您仍然不必修改原始类。

但是,如果底层容器在处理过程中发生变化,则维护并行结构变得更加困难。

答案 3 :(得分:0)

  

在面向对象编程的现代,我在思考   使用继承&amp;多态性。在中使用基类的指针   容器,但后来我必须将派生类的访问器添加到   基类。它有点像......

使用继承时,不需要在基类中放置指向派生类的指针。您只需要转换为派生类。问题是当数据存储在基础对象中时将数据放入派生对象中 - 如果它们是作为派生类型创建的,则只能将它们强制转换,即使您的集合将它们作为基本类型保存。 (如果它们是作为派生类型创建的,那么只需要强制转换!)

因此,如果您有一个BaseC集合,则可以创建一个新类DerivedC,它具有一个带有BaseC的复制构造函数。您可以将BaseC对象复制到其中,对DerivedC对象执行处理,然后将这些对象复制回BaseC对象进行存储。这使用了Flyweight模式。请注意,如果您有一组BaseC对象,则不能只是假装它们是DerivedC类,因为它们没有存储来容纳所有数据成员,您需要创建新的DerivedC对象。

或者,创建一个新类,仅用于包含对基类对象的(智能指针)引用的处理,复制引用,执行处理,在完成后删除处理对象。

答案 4 :(得分:0)

“用户”可以通过模板参数扩展。例如,

template <typename... Extra>
struct User : Extra...
{
    ...
};

struct ExtraData {...};
struct ExtraExtraData {...};

using ExtraUser = User<ExtraData>;
using MoreExtraUser = User<ExtraData, ExtraExtraData>;

答案 5 :(得分:-1)

一个简单的选择是添加一个表示“额外数据”的类型参数......

template<class ExtraDataType>
struct MyExtensibleContainer
{
    ...
    ExtraDataType extra;
};

也许如果你说明为什么这个解决方案还不够,真正的要求就会出现。

int和void *的示例:

struct IntOrVoid
{
};

struct IntOrVoid1 : IntOrVoid
{
    int x;
};

struct IntOrVoid2 : IntOrVoid
{
    void* x;
};

typedef shared_ptr<IntOrVoid> PIntOrVoid;

then use MyExtensibleContainer<PIntOrVoid>

或altenatively:

union IntOrVoid
{
    int x_int;
    void* x_voidp;
};

then use MyExtensibleContainer<IntOrVoid>

您所描述的问题与添加“额外”数据类型无关。您描述的问题与持有可能具有许多异类型之一的变体类型有关。有很多方法可以做到这一点,这是一个更普遍的问题。