17

编辑:为了清楚起见,结构没有做任何事情,因为它没有任何功能。我想我给人的印象是我认为使用初始化列表并将构造函数的主体留空是手头的问题。

假设我正在使用一个结构来保存两个值,并且我有一个构造函数,所以我可以创建一个像这样的任意结构:

struct twoValues
{
    int x;
    int y;

    twoValues(int x_, int y_):y(y_),x(x_)
    {}
};

someFunction(twoValues(1,2));

这使我不必这样做:

twoValues anInstance;
anInstance.x=1;
anInstance.y=2;
someFunction(anInstance);

编辑:你是对的,我也可以使用以下内容进行初始化:

twoValues anInstance = {1,2};

我认为这没有任何问题,但我从 C++ 测试中得到了一些反馈,其中一个负面反馈标记是“不做任何事情的结构的构造函数”。我和那个测试我的人接触有限,所以从来没有问过为什么。

这是一件坏事,为什么?我宁愿继续做。

4

8 回答 8

17

这取决于结构的用途。正如其他人所说,构造函数意味着该类不再是 POD,并且不能对其使用聚合初始化。特别是,您不能在命名空间范围内拥有一些东西,例如:

TwoValues const table[] =
{
    { 1, 2 },
    { 3, 4 },
    // ...
};

你可以有:

TwoValues const table[] =
{
    TwoValues(  1, 2  ),
    TwoValues(  3, 4  ),
    // ...
};

但它更冗长,并且它意味着动态初始化,这可能会导致初始化顺序问题。

另一方面,如果没有构造函数,您将无法动态创建临时实例。代替:

extern void f( TwoValues const& );
//  ...

f( TwoValues( 1, 2 ) );

你必须写:

extern void f( TwoValues const& );
//  ...

TwoValues tmp = { 1, 2 };
f( tmp );

如果对象是动态分配的,那就更糟了,因为您要么必须先分配,然后初始化,要么如上所述创建一个临时对象,然后编写new TwoValues( tmp )并使用隐式复制构造函数。

你必须选择。根据结构的用途,首选其中一个;一方面,我有很多专门用于静态表的结构,并且故意没有构造函数(并且只包含支持静态初始化的类型),并且大量使用它们来配置代码。另一方面,我也有很多类内部的结构,沿着Node树或图中的线;这些几乎总是有一个构造函数,以方便动态创建它们。如果不知道结构在您的应用程序中的作用,就没有“正确”的答案。

于 2012-07-18T09:01:11.473 回答
13

声明一个空的构造函数有副作用..

即使你的构造函数有一个空的主体,它仍然被认为是一个构造函数,因此某些对象属性将会丢失 - 例如对象是 POD。

某些情况需要使用Plain Old Data -types,这可能会导致在没有特定原因的情况下执行您所做的事情是不可取的。

在下一节中阅读有关没有默认构造函数的初始化的更多信息。


&&

您可以使用值初始化您的成员,而无需显式定义“不做任何事情的构造函数”,只需使用以下代码段中的大括号初始化语法。

struct Obj {
  int x, y;
};

Obj a = {1,3}; /* a.x = 1, a.y = 2 */

= { ... }没有构造函数的缺点是在编写C++03时无法在某些情况下使用初始化对象。

C++11为您解决了这个问题,相关示例请参见下一节。


在 C++11 中,使用大括号 ( = { ... }) 进行的初始化被赋予了更多的功能。

从下面的代码片段中可以看出,Obj即使我们使用与本文前面相同的初始化形式,也调用了定义的构造函数。

struct DoubleBoth {
  DoubleBoth (int x, int y)
    : x(x*2), y(y*2) 
  {}  

  int x, y;
};

下面的代码片段在 C++11 之前都是非法的

DoubleBoth a = {1,2}; /* a.x = 2, a.y = 4 */

struct Wrapper {
  Wrapper ()
    : value {3,4}
  {}

  DoubleBoth value;
};

void func (DoubleBoth v = {1,2}) {  // c++11 only
  ...
}

func ({4,5}); // c++11 only, c++03 requires `DoubleBoth obj (0,1); func (obj);`
于 2012-07-18T08:40:55.317 回答
7

您已不再twoValues是 POD,并已阻止结构的实例被默认或值初始化,这通常是理想的属性。就个人而言,如果您需要一种简单的 C++03 友好方法,我更愿意使用免费功能来制作临时实例。

例如

twoValues makeTwoValues(int x_, int y_)
{
    twoValues tmp = { x_, y_ };
    return tmp;
}

void f() {
    someFunction(makeTwoValues(1,2));
}

例如初始化一个类型的成员twoValues

class X {
    twoValues tv;
public:
    X(int x, int y) : tv(makeTwoValues(x, y)) {}
};
于 2012-07-18T08:44:06.167 回答
7

拥有这种构造函数比使用列表初始化要安全得多。

当您使用列表进行初始化时:

twoValues anInstance = {1, 2};

这实际上取决于结构中成员的顺序。在大多数情况下,一开始是完全随机的。

如果另一个程序员碰巧添加了另一个成员,或者按字母对成员进行排序,它将无法正常工作。

拥有一个通过 NAME 将值分配给正确成员的构造函数要安全得多。

于 2012-07-18T08:47:45.703 回答
4

可能是因为你可以这样初始化它:

twoValues anInstance = {1, 2};

但是,如果没有构造函数,您将无法anInstance在 C++03 中的另一个结构的初始化程序列表中初始化 a。例如:

struct Bar {
  twoValues v_;
  Bar() : v_(1,2) {} // error!
  Bar() { v_ = {1,2}; } // have to assign value in constructor body
  Bar() : v_{1,2} {} // OK in C++11
};

所以实际上构造函数做了一些事情,它在 C++03 中确实起到了非常有用的作用。在 C++11 中这不是一个问题。

于 2012-07-18T08:40:46.960 回答
2

这个构造器没有任何问题。

此外,它会做一些事情,它会影响 x 和 y 的值,这是我对这样一个构造函数的期望。

但是,也许你的公司有一些编码规则说你应该用另一种方式来做。在这种情况下,我建议您询问给您反馈的人并进行必要的修改。

于 2012-07-18T08:42:02.220 回答
2

构造函数使结构成为非 POD,这在某些情况下可能是不可取的。

如果您的结构没有构造函数,那么您的结构将是 POD 1,并且您将被允许像这样初始化:

twoValues anInstance = {1,2}; //C++03 and C++11 both!
twoValues anInstance {1,2};   //C++11 only

哪个好。

1.实际上还有其他的东西使结构非 POD。因此,仅仅因为您的结构没有构造函数并不一定意味着它是 POD。你想看看这篇文章来了解其他的东西是什么。

于 2012-07-18T08:42:27.810 回答
0

结构可以有构造函数,语法与 C++ 中的类相同。类和结构之间的区别在于,类成员默认是私有的,而结构成员默认是公共的,但是联合不允许在结构中使用构造函数。不过,您可以在联合上创建一个构造函数。

于 2012-07-18T08:43:23.050 回答