静态成员和默认构造函数 C++

9

我在书中看到了这样一句话:

当你声明一个静态成员时,你不需要初始化它;如果你没有这样做,C++会调用默认构造函数。

这让我很困惑,它是指仅限于对象成员吗?如果是,那么在什么时候会调用默认构造函数呢?此外,如果没有默认构造函数,如何初始化静态成员对象?


1
听起来非常不正确。你在哪本书中读到的? - ildjarn
4个回答

18

让我们来分解一下。假设有一个class Foo;在某个地方。现在我们将它作为我们类的静态成员。

class Star
{
  static Foo z;
  // ...
};

基本上,这声明了一个全局对象Foo Star :: z--那么它是如何实例化的呢?标准告诉你:它被默认构造。但请记住,在你的翻译单元之一中必须提供实际的对象实例:

// in, say, star.cpp
Foo Star::z;  // OK, object lives here now

现在假设 Foo 实际上没有默认构造函数:

class Foo
{
public:
  Foo(char, double); // the only constructor
  // ...
};

现在有一个问题:我们如何构造Star::z?答案是“就像上面一样”,但现在我们必须调用特定的构造函数:

// again in star.cpp
Foo Star::z('a', 1.5);


标准实际上有两个不同的"初始化"(语法概念)和"构造"(函数调用)的概念,但我认为我们现在不需要进入这个问题。


如果静态成员是一个 int,我在 cpp 中只写了 int Star::z;,它会被默认初始化为 0 吗? - 463035818_is_not_a_number
1
@tobi303:是的,如果静态存储期对象没有被常量初始化,它们将首先进行零初始化(即使它们稍后会动态初始化)。 - Kerrek SB

2
在C++中,新对象总是以某种方式进行初始化。有默认初始化、复制初始化、值初始化和直接初始化等方式,唯一的问题是你的代码使用哪种方式。
我认为他们的意思是:
SomeObject SomeClass::x; // default initialization, class types will have the default constructor called

vs

SomeObject SomeClass::x = blah; // copy initialization

复制构造函数是必需的,可能还存在将 blah 转换为临时 SomeObject 之后调用复制构造函数,有时会跳过调用复制构造函数的步骤,但它必须是可访问的。如果不想调用默认或复制构造函数,请使用直接初始化语法:
SomeObject SomeClass::x(blah); // direct initialization

他说的是“声明”,而不是“定义”,这就使问题变得很困惑。 - ildjarn
1
@ildjarn:定义是声明的一种。但这本书显然不准确。 - Ben Voigt
对于使用ODR的静态数据成员,定义必须始终与声明分开,因此我认为在这种情况下这并不相关。但是,是的,我很想知道是哪本书在传播这种错误信息。 - ildjarn
这本书是《C++入门指南》,我很高兴它对其他人来说一点也不好懂,因为对我来说确实也是这样 :-/ - rubixibuc
3
@rubixibuc :“for Dummies”系列的书似乎都是由蠢货写的。;-]我建议你把它扔掉,然后去看看这个FAQ中列出的一本或多本书。 - ildjarn
谢谢 :-) 我会查看列表的 - rubixibuc

1
在这个语句中: “当你声明一个静态成员时,你不需要初始化它;如果你没有初始化,C++会调用默认构造函数。” “静态成员”指的是某些基本类型成员变量的“静态成员”,换句话说,“静态成员”的数据类型应该只有:int、float或char…… 因此对于这些基本类型,编译器知道它们的“默认构造函数”,例如对于“int”类型,编译器只需设置为0。 但是,如果这些静态成员的类型是特定的类,而不是那些C++内置的基本类型,你必须显式调用特定的构造函数,这与“Kerrek SB”提供的第一个答案完全相同。 但要记住一件事,即使您的静态成员的类型是基本类型,您仍然需要在*.c文件中声明其实现。 假设在“Kerrek SB”提供的示例中,如果我们更改:

class Star
{
  static Foo z;
  // ...
};

class Star
{
  static int z;
  // ...
};

在 star.h 文件中,我们仍需要在 star.c 中添加一行代码:
// again in star.cpp
int Star::z;

编译器会将其初始化为0;因此,我相信“静态成员”中的“静态”只是指C++基本类型的“静态成员”。如果其类型是某些类,则必须调用它们特定的构造函数,就像调用任何其他类对象一样。

1
你暗示编译器不能自动调用用户定义类型的默认构造函数,这是错误的。它当然可以,并且经常这样做。UDT的默认构造函数可以通过定义 SomeObject myObj; 隐式调用,也可以通过显式定义 SomeObject myObj{}; // 或 () 调用。 - underscore_d
underscore_d,我没有这么说。本话题是在谈论另一个类中的静态成员类型,而不是一般变量类型的初始化。如果你相信“static SomeObject myObj”可以在你的另一个类中定义,请设计一个小测试来证明它。我已经做过了,但它不能正常工作并报告编译错误。 - Clock ZHONG

1

编辑:将评论移至答案。

member 的定义总是一个 对象成员,因为它意味着作为对象成员声明的变量。也就是说,静态成员需要进行前向声明,并且通常最好显式初始化变量。这是哪本书?

// SomeObject.h
class SomeObject
{
public:
   SomeObject(); // default constructor
private:
   // declare members
   int m_intMember;
   static int m_staticIntMember;
};

// SomeObject.cpp
#include "SomeObject.h"

// forward declaration and initializing of static member
int SomeObject::m_staticIntMember(42); 

SomeObject::SomeObject() : m_intMember(7) // initializing other member
{
}

有没有一个术语来描述当你像这样初始化int时“int a(45)”; 还是只是另一种方式?与 int a = 5 相反。 - rubixibuc
1
Ben Voight在他的回答中称其为“直接初始化”。这只是我通常更喜欢的选项。 - AJG85
@AJG:这是标准中使用的术语。 - Ben Voigt
@Ben Voight:我知道,我是在引导他去看你的答案了解详情。 - AJG85

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接