好的,为C ++中的无行为聚合(bundle-o-data)提供构造函数?

时间:2010-01-11 17:00:28

标签: c++

请参阅C++ Coding Standards的规则#41或Sutter的Gotw #70,其中声明:

  

使数据成员保密,除了无行为聚合(C风格的结构)。

为了方便起见,我经常想为这些C风格的结构添加一个简单的构造函数。例如:

struct Position
{
    Position(double lat=0.0, double lon=0.0) : latitude(lat), longitude(lon) {}
    double latitude;
    double longitude;
};

void travelTo(Position pos) {...}

main()
{
    travelTo(Position(12.34, 56.78));
}

虽然可以更容易地动态构造一个Position,但构造函数也可以为我初始化默认的Position对象。

也许我可以关注std :: pair的例子并提供一个“makePosition”免费功能? NRVO应该和构造函数一样快,对吗?

Position makePosition(double lat, double lon)
{
    Position p;
    p.latitude = lat;
    p.longitude = lon;
    return p;
}

travelTo(makePosition(12.34, 56.78));

我是否违背了“无行为聚合”概念的精神,通过添加那个可怜的小构造函数?

修改

是的,我知道Position p={12.34, 56.78}。但我不能用纯C结构travelTo({12.34, 56.78})做。{/ p>

编辑2:

对于那些对POD类型感兴趣的人:What are POD types in C++?

后续: 我问了一个与此问题密切相关的后续问题here

5 个答案:

答案 0 :(得分:7)

我们定期为我们的聚合类型定义构造函数,没有任何不利影响。事实上,我能想到的唯一不利影响是,在性能危急的情况下,你无法避免默认初始化,也不能在联合中使用类型。

替代方案是大括号的大括号样式

Position p = {a,b};

或免费的“制作”功能

Position makePosition(double a, double b)
{
    Position p = {a,b};
    return p;
}

前者的问题是你不能用它来实例化一个临时的传递给函数

void func(Position p)
{
    // ...
}

// func({a,b}) is an error

后者在这种情况下很好,但对于懒惰的程序员来说,打字的次数要多一些。 后一种形式(make函数)的问题在于它可能会忘记初始化您的数据结构。因为未初始化的变量让我感到相当不舒服,所以我更喜欢为我的聚合类型定义构造函数。

std :: make_pair存在的主要原因实际上不是出于这个原因(std :: pair有构造函数),但实际上因为要调用模板类型的构造函数,你必须传递模板参数 - 这很不方便:

std::pair<int,int> func()
{
    return std::pair<int,int>(1,2);
}

最后,在您的示例中,您至少应该使构造函数显式

explicit Position(double lat=0.0, double lon=0.0)

否则你允许从双重

隐式转换为位置
Position p = 0.0;

可能会导致意外行为。实际上我会定义两个构造函数,一个用于初始化为零,另一个用两个值初始化,因为如果没有纬度和经度,Position构造可能没什么意义。

答案 1 :(得分:5)

我经常为结构提供构造函数,没有问题。但是,如果构造函数是“非平凡的”,那么结构不再被认为是POD类型,并且将对您可以使用它做什么进行限制。如果这对你来说是一个问题(对我来说从来没有),那么make_XXXX功能显然是要走的路。

答案 2 :(得分:2)

请注意,如果没有构造函数,以下内容已经满足您的需求:

int main()
{
  Position pos = { 12.34, 56.78 };
  travelTo(pos);

  Position pos2 = {}; // zero initialises
  travelTo(pos2);
}

答案 3 :(得分:2)

从概念上讲,它很好 - 你不会违背“无行为聚合”的精神。问题是结构不再是POD类型,因此标准对其行为的保证较少而且它不能存放在工会中。

你有没有考虑过这个?

Position p = {12.34, 56.78};

答案 4 :(得分:0)

自从我发布了这个问题以来,我一直在为我的聚合定义构造函数而陷入困境。使用上面的位置示例,GCC在我这样做时会抱怨:

const Position pos[] = 
{
    {12.34, 56.78},
    {23.45, 67.89},
};

警告:扩展初始化程序列表仅适用于-std = c ++ 0x或-std = gnu ++ 0x |

相反,我必须这样做:

const Position pos[] =
{
    Position(12.34, 56.78),
    Position(23.45, 67.89)
};

通过这种解决方法,我担心在嵌入式系统中,我的常量表不会存储在闪存/ ROM中。

修改

我尝试删除了位置构造函数,并且pos数组确实已从.bss移动到.rodata段。