7

我使用结构来表示写入文件的数据。如果我需要向这个结构添加成员(即保存额外的数据),我会创建一个从原始结构派生的新结构(这代表数据集的新版本)。例如:

struct data1
{
   int stuff1;
   int stuff2;
};

struct data : data1
{
   int stuff3;
};

通过检查我们是否正在加载来保持向后兼容性,如果是data1,则将其转换为data(并且仅对 中的那些新成员进行值初始化data)。最好的方法是什么?这是我开始的:

if( loaded_data.size() == sizeof(data1) ) {
    // Old data format detected, upgrade to new structure
    data d = data(); // value-initialize everything
    // TODO: Assign 'data1' to 'data'
}

我曾想过将构造函数放入data从 a 中复制data1,但这会破坏我得到的自由值初始化(那时我必须实现自己的默认构造函数)。我应该使用 memcpy(),还是有更好的内置方式(可能是一些复制语义的技巧)?

4

4 回答 4

4
#include <cstdio>

struct A {
  int a;
};

struct B : A {
  int b;
};

int main()
{
  A a;
  a.a = 42; 
  B b;
  b.a = 0;
  b.b = 42;
  (A&)b = a;

  printf("%s\n", b.a == b.b?"it works":"nope");
}
于 2017-09-13T08:13:41.660 回答
2

我不喜欢这个设计,我相信你不应该继承(例如,这会使你的类型不是聚合),并且有比检查实体大小更好的序列化/反序列化方法。话虽如此,在您强制初始化后,您可以分配。将定义隐式声明的复制构造函数并复制成员:

data d = { 1, 2 };         // Can use this as it is an aggregate
data1 d1 = data1();
d1 = d;

同样,我会完全避免这种情况......

如果您使用组合而不是继承,那么您可以使用聚合初始化并避免对基进行双重初始化data1

struct data1 {
  data base;
  int extra;
};
data d   = { 1, 2 };
data1 d1 = { d, 5 };   // or { d, 0 }

我曾考虑将构造函数放入数据中以从 data1 复制,但这会破坏我得到的自由值初始化(那时我必须实现自己的复制和默认构造函数)。

不完全正确。您将需要提供一个默认构造函数,因为构造函数的存在data将禁止默认构造函数的隐式声明。但是复制构造函数仍然会隐式生成。

于 2012-09-18T19:37:58.453 回答
1

您希望为您的成员提供默认 ctor,但仍希望能够利用默认副本 ctor。我想你可以用组合来做到这一点。

struct Data1 {
  /**/
};

struct Data2 { // this is your extension
  /**/
};

struct Data3 { // possibly the next extension.
  /**/
};

template <typename Base, typename Derived>
struct ExtendData : Base, Derived {
  ExtendData(Base const& base, Derived const& derived)
    : Base(base), Derived(derived) {}
};

typedef ExtenData<Data1,Data2> FullData2;
typedef ExtenData<FullData2,Data3> FullData3;

这允许您使用 PODstruct的所有默认构造函数:

FullData2 data2 = {Base{/**/},Derived{/**/}};
于 2012-09-18T19:43:30.870 回答
0

如果您想避免/隐藏 的丑陋memcpy(),您可以创建全局函数来初始化您的结构 - 在这些函数中,您可以使用memcpy()或简单地一个一个地分配字段。

这样,如果您的初始化语义发生变化,您只需要更新您的初始化函数并保持结构中的成员函数干净(也就是说,如果您喜欢/需要,它们仍然看起来像普通的数据结构)。例如:

void InitData ( data& oData, data1& oInitializer )
{
    memcpy ( ... );
}

当您不断添加新成员和新结构时,您可以为每种新类型覆盖此函数。

如果您更喜欢(有点)类似 C++ 的方法,可以将这些函数转换为data结构的公共静态成员。然后你会称它为

data::InitFrom ( data1 );

这使您可以保持结构干净(没有虚函数等),但您仍然可以获得更好的类型安全性,并且低级细节仍然对其余代码隐藏。这些全局/静态函数还将保留结构的当前语义。

但请记住,C++ 纯粹主义者可能不喜欢这个选项。

于 2012-09-18T19:43:38.437 回答