代码之家  ›  专栏  ›  技术社区  ›  Emile Cormier

可以为C++中的无行为聚集(捆绑-O-DATA)提供构造函数吗?

c++
  •  5
  • Emile Cormier  · 技术社区  · 15 年前

    请参阅第41条规则 C++ Coding Standards 还是萨特的 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));
    }
    

    虽然使动态构造位置更容易,但构造函数也可以为我初始化默认位置对象。

    也许我可以按照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} .但我做不到 travelTo({12.34, 56.78}) 纯C结构。

    编辑2:

    对于那些好奇豆荚类型的人: What are POD types in C++?

    随访: 我问了一个后续问题 here 这与这一点密切相关。

    5 回复  |  直到 14 年前
        1
  •  7
  •   Adam Bowen    15 年前

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

    可供选择的是初始化的大括号样式

    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_对存在的主要原因实际上不是因为这个原因(std::pair有构造函数),而是因为要调用模板类型的构造函数,必须传递模板参数-这不方便:

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

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

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

    否则,您允许从double向位置隐式强制转换

    Position p = 0.0;
    

    这可能会导致意外行为。实际上,我将定义两个构造函数,一个初始化为零,另一个初始化为两个值,因为如果没有纬度和经度,位置构造可能没有多大意义。

        2
  •  5
  •   anon    15 年前

    我通常为结构提供一个构造函数,没有问题。但是,如果构造函数是“非平凡的”,那么该结构就不再被认为是pod类型,并且对您可以使用它做什么有限制。如果这对你来说是一个问题(我从来没有遇到过),那么一个make XXXX函数显然是一个可行的方法。

        3
  •  2
  •   James Hopkin    15 年前

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

    int main()
    {
      Position pos = { 12.34, 56.78 };
      travelTo(pos);
    
      Position pos2 = {}; // zero initialises
      travelTo(pos2);
    }
    
        4
  •  2
  •   JoeG    15 年前

    从概念上讲,这很好——你不会违背“无行为聚合”的精神。问题是结构不再是pod类型,因此标准对其行为的保证更少,并且不能存储在联合中。

    你考虑过这个吗?

    Position p = {12.34, 56.78};
    
        5
  •  0
  •   Emile Cormier    14 年前

    自从我提出这个问题以来,我一直在为我的聚合定义构造函数。使用我上面的位置示例,当我这样做时,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)
    };
    

    有了这个解决方案,我担心在嵌入式系统中,我的常量表不会存储在flash/rom中。

    编辑:

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