【问题标题】:Constructor initialization构造函数初始化
【发布时间】:2013-03-22 06:43:15
【问题描述】:

我正在阅读 Andrew Koenig 和 Barbara E. Moo 的“Accelerated C++”,我正在阅读关于构造函数 (5.1) 的章节。

他们在这里提到

我们说过构造函数的存在是为了确保创建对象时其数据成员处于合理状态。一般来说,这个设计目标意味着每个构造函数都应该初始化每个数据成员。给成员一个值的需要对于内置类型的成员来说尤其重要。 ...

虽然我们只显式地初始化了midtermfinal,但其他数据成员是隐式初始化的。具体来说,nstring默认构造函数初始化,homeworkvector默认构造函数初始化。

他们正在谈论的课程是

class Student_info {
public:
    std::string name() const (return n;}
    bool valid() const {return !homework.empty();}
    std::istream& read(std::istream&);

    double grade() const;
private:
    std::string n;
    double midterm, final;
    std::vector<double> homework;
};

它们的默认构造函数是

Student_info::Student_info(): midterm(0), final(0) {}

我想澄清一下,这意味着像intdouble 这样在术语之前没有std:: 的东西需要专门初始化?

【问题讨论】:

  • "需要特别地进行初始化" - 你实际上可以省略 specific 值,它们仍然会被初始化 所有这些基本类型有一个零值。 : midterm() 会将成员设置为 0.0,因为这是 double 的零值。类似地,如果您将 int 成员放在初始化列表中,但没有提供初始值,它将被初始化为 0

标签: c++ class constructor


【解决方案1】:

没错。
但 std:: 不是你要找的。​​p>

任何基本类型都不会被初始化,除非你明确地这样做。

所以char/int/float/pointers 等等。另外(如下面的 Ian 所指出的)任何没有显式构造函数的类/联合都将默认初始化其成员(这意味着基本(对于没有显式构造函数的类/联合的成员,递归)它们未定义)。

附注:

  • 此规则适用于自动和动态存储期限对象
  • 静态和线程存储持续时间对象初始化为零。

【讨论】:

  • 没有构造函数的类、结构或联合成员也不会初始化它包含的任何基本类型
  • @IanM_Matrix1:你是绝对正确的(假设默认而不是零初始化)。
【解决方案2】:

intdouble 是内置类型,而不是类,因此它们没有默认构造函数,并且默认情况下未定义。例如:

int a;
Student_info s;

两个变量的语法相同,但a的值是未定义的,而s的值是定义的,因为Student_info s实际上调用了构造函数,即Student_info s = Student_info();

【讨论】:

  • a 已定义但未初始化
  • 谢谢,我的意思是“a 的值”是未定义的。
【解决方案3】:

作者只是试图展示一个默认构造函数,其中所有内容都默认为 0。对于字符串和向量,它们只是空的。对于 int 和 double 等原始类型,这些原始类型在声明期间的初始值默认为变量指向的内存中的值。这确实取决于编译器

【讨论】:

  • 该值不是默认值。它是未定义的。读取未初始化的变量是未定义的行为。
  • 好吧,我猜我的教授当时骗了我。无论如何,这取决于编译器。对于古老的 C 和 C++ 编译器,它可能是未定义的,但我目前使用的那个默认值是 0
  • 没有。你错过了你的教授所说的(这个值是随机的,取决于什么是记忆,因此是随机的)。但是变量处于未定义状态。该标准说在未定义状态下使用变量是不好的。您看到 0 是因为您在调试模式下编译(这通常会初始化内存,因为这会使调试更容易)。在释放模式下,它不会初始化内存,除非您明确地这样做以获得额外的性能。
【解决方案4】:

它与 std 命名空间无关。 n 和 homework 是类,它们的构造函数会在 Student_info 的构建过程中被调用。但是 midterm 和 final 是原始值,它们没有构造函数。在构造函数中初始化原始成员不是必需的,而是一种很好的方式。

【讨论】:

    【解决方案5】:

    还有更多。

    1) 对于原始或内置类型,隐式初始化(或编译器初始化)意味着无操作(什么都不做)。

    2)对于原始或内置类型,显式初始化(或由程序员初始化)是显而易见的:)

    如您的示例:

        Student_info::Student_info(): midterm(0), final(0) {}
    

    3) 对于非原始类型,隐式初始化(或编译器初始化)意味着编译器可能(或可能不)合成一个构造函数用于初始化。

     Usually, a constructor is synthesized by the compiler under the following cases:
    
     a) The non-primitive type is polymorphic or derives from some polymorphic/non-polymorphic types,
     b) The non-primitive type has a polymorphic/non-polymorphic member. 
    
     Compiler synthesized constructor will usually have code to 
    
     a) initialize v-pointer to v-table, 
     b) call base class constructor, 
     c) call constructors of members, so on, 
    
     basically driven by the definition of non-primitive type.
    

    4) 对于非原始类型,显式初始化(或由用户提供的构造函数初始化)意味着编译器将调用用户定义的构造函数,而不是为了初始化而合成一个构造函数。

    例如,由各自的非原始类型(如 std::string 或 std::vector)定义的默认构造函数被调用。

    **

    注意:编译器仍然可以在用户定义的内部增加代码 构造函数根据需要进行一些幕后初始化 (请查看上面的第 3 步以了解此类需求)。这种增强的代码将始终 插入到构造函数内部自定义代码之前!

    **

    【讨论】:

    • 几乎(非常)好,但是您正在搞乱默认构造(“编译器初始化”)和 def 常量的编译器生成。显式初始化 - 您显式调用构造函数(无论确切是什么,可以是 def constr,甚至是编译器生成的)和隐式或 def const,编译器为您调用 def constr,无论它是由编译器自动生成还是用户自定义。默认构造函数很简单,没有参数。
    • 这是我试图解释的一个极端例子:stackoverflow.com/a/15416693/1458030
    • 抱歉您的评论有点混乱。 “”你搞砸了默认构造(“编译器初始化”)“”=>默认构造并不总是编译器初始化。当我们提供默认构造函数(没有任何参数的构造函数)时,这一点变得很明显。每当发生默认构造时,编译器必然会调用用户定义的默认构造函数。
    • 我同意您的“显式”与“隐式”初始化,但不太明白我的帖子如何与之相矛盾!我只是通过使用编译器生成的或用户定义的构造函数解释了初始化是如何在幕后发生的。此外,大多数书籍并没有具体指出合成默认构造函数背后的原因。我只是想在这里解释一下。
    • 这里有一些更详细的描述:stackoverflow.com/questions/9635772/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 2018-08-15
    • 2018-06-30
    • 2019-10-07
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多