在C ++中执行构造函数

时间:2011-09-10 12:30:30

标签: c++ constructor

  

构造函数的执行分两个阶段完成:

     
      
  1. 初始化阶段

  2.   
  3. 正文执行阶段,由构造函数体内的所有语句组成。请注意,类型的数据成员始终在初始化阶段初始化,无论该成员是否在构造函数初始化列表中显式初始化。初始化发生在构造函数体的任何语句执行之前。

  4.   

让我们考虑一种通过构造函数初始化 class student 实例的方法 -

Student(string &fn, string &ln, int i, int y = Freshman)
    : first_name(fn)
    , last_name(ln)
    , id(i)
    , year(y)
{}

这是另一种,但“低效”和“不优雅”的做法 -

Student(string &fn, string &ln, int i, int y = Freshman)  
 {
     first_name = fn;
     last_name = ln;
     id = i;
     year = y; 
 }
  

新代码中的此构造函数(上面的代码)分配类Student的成员。它没有明确初始化它们。是否存在显式初始值设定项,即使在执行构造函数之前,也会初始化first_name和last_name成员。此构造函数隐式使用默认字符串构造函数来初始化first_name和last_name成员。执行构造函数的主体时,first_name和last_name成员已具有值。这些值将被构造函数体内的赋值覆盖。

所以,这意味着当执行到达构造函数的左括号时,这就是条件 -

  • 'first_name',即字符串,通过调用默认字符串构造函数(编译器'生成'的那个)进行初始化。
  • 'last_name',即字符串,通过调用默认字符串构造函数(编译器'生成')来初始化。
  • 'id',整数 未初始化
  • 'year',这是整数 未初始化

现在,很明显,赋值是在构造函数体中对四个变量中的每一个进行的。

我对这件事的理解是对的吗?我不知何故觉得我错过了什么。

此外,在使用初始化列表时,是默认构造函数(编译器为我们生成的构造函数),使用我们的参数调用并传递(在 string 的情况下)和初始化完成,如在“int id = i;”中,如果是id(i)??

PS:大部分报价均来自此链接 -

http://www.bogotobogo.com/cplusplus/constructor.php

3 个答案:

答案 0 :(得分:7)

您的理解大多是正确的,除非您必须初始化初始化中的const成员,因为您无法分配给他们(因为他们是const)构造函数的主体或其他任何地方。如果您尝试将const成员保留为未初始化状态,则会出现编译错误。

是的,在初始化列表中,调用与您提供的参数匹配的对象的构造函数。如果没有参数(例如: blah()),则调用默认构造函数。

另外,在某些情况下,绝对必须使用初始化列表(它不是可选),例如

  • 当班级有const名成员
  • 当类是没有默认构造函数的类的直接子级时
  • 当班级有参考成员时

答案 1 :(得分:1)

你没有说你的Student类是如何定义的,但你可能会对一件事情感到困惑:在你的第二个“不优雅”的版本中,first_name不一定是一个常量字符串 - 相反,fn是对const字符串的引用,您将的值分配给您的成员变量first_name

现在,转到初始化列表:这些的全部内容是指定成员对象的哪些构造函数被调用。如果没有列表,则所有成员对象都会调用其默认构造函数(或者更准确地说,它们是默认初始化的)。但是,您在初始化列表中提到的任何对象都将 构造函数(或更确切地说是“初始化程序”)称为,而不是

初始化列表不仅仅是装饰。这是必不可少的,因为成员对象和子对象甚至可能不是默认构造的,因此必须提供初始化程序 。 const成员实际上是一个非常好的例子:

struct Foo
{
  const int n;
  Foo();  // error! What is n?
  Foo(int m) : n(m) { } // OK
};

这是另一个例子,这次是从没有默认构造函数的基类派生的:

struct Bar : Foo
{
  Bar();  // error! How to initialize Foo?
  Bar() : Foo(5) { } // OK, now Bar::n == 5
};

答案 2 :(得分:1)

  

此外,在使用初始化列表时,是默认的构造函数(编译器为我们制作的),使用我们的参数调用并传递(如果是字符串),并在“int id = i;”中完成初始化,以防万一id(i)??

首先,默认构造函数不一定是“编译器为我们制作的”。它们只是没有参数的构造函数,例如Student()。有时编译器会自动生成一个,例如int,有时必须编写,就像std::string一样。

考虑它的简单方法是:如果存在初始值设定项,则调用相应的构造函数。因此,假设first_namestd::string,则初始化程序first_name(fn)会使first_name结构std::string::string(const std::string&),并将fn作为参数传递。如果初始值设定项使用,则first_name在执行std::string::string()代码之前使用{ ... }构建,然后在您编写first_name = fn;时生成std::string::operator =(const std::string&)在致电fn时,将{{1}}作为参数传递。

最后,关于初始化器的重要一点是,它们按照成员在类中声明的顺序执行,按照在构造函数中编写它们的顺序执行。此外,基类构造函数的初始化程序在成员初始化之前发生。