在C++中执行构造函数



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

  1. 初始化阶段

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

让我们考虑一种通过构造函数初始化类学生实例的方法

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"是整数未初始化

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

我对这件事的理解正确吗?不知怎么的,我觉得我错过了什么

此外,在使用初始化列表时,默认构造函数(编译器为我们制作的构造函数(是否被调用并与我们的参数一起传递(在字符串的情况下(,以及在id(i(的情况下是否如"int id=i;">中那样进行初始化??

PS:大多数报价都来自这个链接-

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

您的理解基本上是正确的,只是您必须在初始化器中初始化const成员,因为您无法在构造函数的主体或其他任何位置为它们赋值(因为它们是const(。如果您试图使const成员未初始化,则会出现编译错误。

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

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

  • 当类具有const成员时
  • 当类是没有默认构造函数的类的直接子级时
  • 当类具有引用成员时

您并不是在说Student类是如何定义的,但您可能会对一件事感到困惑:在第二个"不雅"版本中,first_name不一定是const字符串,相反,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
};

此外,在使用初始化列表时,默认构造函数(编译器为我们制作的构造函数(是否被调用并与我们的参数一起传递(在字符串的情况下(,以及在id(i(的情况下是否按照"int id=i;"中的方式进行初始化??

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

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

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

最新更新