请参阅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。
答案 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段。