2

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

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

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

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

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

4

6 回答 6

3

在不损害对象本身完整性的情况下存储有关对象的额外数据的最佳方法是在容器中存储一对数据。

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

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

std::vector<UserAndExtraData> vector;
于 2012-04-07T14:10:48.947 回答
2

我会研究Decorator Pattern。您可以在处理对象时装饰它们,然后将装饰的对象扔掉。如果有很多共享数据,您还可以查看FlyWeight 模式

于 2012-04-07T14:10:05.860 回答
1

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

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

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

using ExtraUser = User<ExtraData>;
using MoreExtraUser = User<ExtraData, ExtraExtraData>;
于 2019-06-07T21:02:46.107 回答
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.
}

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

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

于 2012-04-07T14:23:44.533 回答
0

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

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

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

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

于 2012-04-07T14:33:57.643 回答
-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>

或者:

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

then use MyExtensibleContainer<IntOrVoid>

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

于 2012-04-07T14:21:11.697 回答