请参阅C++编码标准的规则#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));
}
Run Code Online (Sandbox Code Playgroud)
虽然可以更容易地动态构造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));
Run Code Online (Sandbox Code Playgroud)
通过添加那个可怜的小构造函数,我是否违背了"无行为聚合"概念的精神?
编辑:
是的,我知道Position p={12.34, 56.78}.但我不能travelTo({12.34, 56.78})用纯C结构.
编辑2:
对于那些对POD类型感兴趣的人:C++中的POD类型是什么?
追问: 我问一个后续问题这里是密切相关的这一个.
我们定期为我们的聚合类型定义构造函数,没有任何负面影响.事实上,我能想到的唯一不利影响是,在性能危急情况下,您无法避免默认初始化,也无法在联合中使用该类型.
替代方案是初始化的大括号样式
Position p = {a,b};
Run Code Online (Sandbox Code Playgroud)
或免费的"制造"功能
Position makePosition(double a, double b)
{
Position p = {a,b};
return p;
}
Run Code Online (Sandbox Code Playgroud)
前者的问题是你不能用它来实例化一个临时传递给一个函数
void func(Position p)
{
// ...
}
// func({a,b}) is an error
Run Code Online (Sandbox Code Playgroud)
后者在这种情况下很好,但对于懒惰的程序员来说,输入的次数要多一些.后一种形式(make函数)的问题在于它可能会忘记初始化您的数据结构.因为未初始化的变量让我感到相当不舒服,所以我更喜欢为我的聚合类型定义构造函数.
std :: make_pair存在的主要原因实际上不是出于这个原因(std :: pair有构造函数),但实际上因为要调用模板类型的构造函数,你必须传递模板参数 - 这很不方便:
std::pair<int,int> func()
{
return std::pair<int,int>(1,2);
}
Run Code Online (Sandbox Code Playgroud)
最后,在您的示例中,您至少应该使构造函数显式化
explicit Position(double lat=0.0, double lon=0.0)
Run Code Online (Sandbox Code Playgroud)
否则你允许从double隐式转换为Position
Position p = 0.0;
Run Code Online (Sandbox Code Playgroud)
这可能会导致意外行为.实际上我会定义两个构造函数,一个用于初始化为零,另一个用两个值初始化,因为如果没有纬度和经度,Position构造可能没有多大意义.
小智 5
我经常为结构提供一个构造函数,没有任何问题.但是,如果构造函数是"非平凡的",那么结构不再被认为是POD类型,并且将对您可以使用它做什么进行限制.如果这对你来说是个问题(对我来说从来没有),那么make_XXXX功能显然是要走的路.