基类与派生类中析构函数与构造函数的顺序?

成员构造函数和析构函数调用的顺序

哦,C ++专家们,我寻求您的智慧。与我说标准话,并告诉我C ++是否保证以下程序:

换句话说,是否保证成员按声明顺序初始化并以相反的顺序销毁?

换句话说,是否保证成员按声明顺序初始化并以相反的顺序销毁?双方都同意。见12.6.26初始化应按以下顺序进行:首先,并且仅对于如下所述的最大派生类的构造函数,虚拟基类应按照它们在基类的有向无环图的深度优先从左到右遍历时出现的顺序进行初始化,其中“左“从右到右”是基类名称在派生类base-specifier-list中的出现顺序。然后,直接基类应按照它们出现在base-specifier-list中的声明顺序进行初始化(与mem-initializers的顺序无关)。然后,非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样,无论mem-initializer的顺序如何)。最后,执行构造函数主体的复合语句。[注意:声明顺序是强制执行的,以确保以相反的初始化顺序销毁基础和成员子对象。—尾注]

是的,它们是(是非静态成员)。初始化(构造)请参见12.6.2 / 5,销毁请参见12.4 / 6。

b(a);}如果a是之前破坏b,然后b将举行无效成员引用。通过以创建对象的相反顺序破坏对象,我们保证正确破坏。

打开App,查看更多内容

}

构造函数是一种可初始化其类的实例的成员函数。构造函数具有与类相同的名称,没有返回值。构造函数可以具有任意数量的参数,类可以具有任意数量的重载构造函数。构造函数可以具有任何可访问性(公共、受保护或私有)。如果未定义任何构造函数,则编译器会生成不采用任何参数的默认构造函数;可以通过将默认构造函数声明为已删除来重写此行为。
构造函数顺序 构造函数按此顺序执行工作:
按声明顺序调用基类和成员构造函数。
如果类派生自虚拟基类,则会将对象的虚拟基指针初始化。
如果类具有或继承了虚函数,则会将对象的虚函数指针初始化。虚函数指针指向类中的虚函数表,确保虚函数正确地调用绑定代码。
它执行自己函数体中的所有代码。
下面的示例显示,在派生类的构造函数中,基类和成员构造函数的调用顺序。首先,调用基构造函数,然后按照基类成员在类声明中出现的顺序对这些成员进行初始化,然后,调用派生构造函数。



如果构造函数引发异常,析构的顺序与构造的顺序相反:
构造函数主体中的代码将展开。
基类和成员对象将被销毁,顺序与声明顺序相反。
如果是非委托构造函数,所有完全构造的基类对象和成员均将被销毁。但是,对象本身不是完全构造的,因此析构函数不会运行。
成员列表 使用成员初始值设定项列表从构造函数参数初始化类成员。此方法使用直接初始化,这比在构造函数体内使用赋值运算符更高效。



显式构造函数 如果类具有带一个参数的构造函数,或是如果除了一个参数之外的所有参数都具有默认值,则参数类型可以隐式转换为类类型。例如,如果 Box 类具有一个类似于下面这样的构造函数:


可以初始化 Box,如下所示:

或将一个 int 传递给采用 Box 的函数:



这类转换可能在某些情况下很有用,但更常见的是,它们可能会导致代码中发生细微但严重的错误。作为一般规则,应对构造函数使用 explicit 关键字(和用户定义的运算符)以防止出现这种隐式类型转换:


默认构造函数 默认构造函数没有参数;它们遵循略有不同的规则:
默认构造函数是一个特殊成员函数;如果没有在类中声明构造函数,则编译器会提供默认构造函数:


当你调用默认构造函数并尝试使用括号时,系统将发出警告:


这是“最棘手的解析”问题的示例。这种示例表达式既可以解释为函数的声明,也可以解释为对默认构造函数的调用,而且 C++ 分析器更偏向于声明,因此表达式会被视为函数声明。
如果声明了任何非默认构造函数,编译器不会提供默认构造函数:


如果类没有默认构造函数,将无法通过单独使用方括号语法来构造该类的对象数组。例如,在前面提到的代码块中,框的数组无法进行如下声明:


但是,你可以使用初始值设定项列表将框的数组初始化:


复制和移动构造函数 复制构造函数是特殊成员函数,它采用对相同类型对象的引用作为输入,并创建它的副本。移动也是特殊成员函数构造函数,它将现有对象的所有权移交给新变量,而不复制原始数据。

显式默认构造函数和已删除构造函数 你可以显式设置默认复制构造函数、设置默认构造函数、移动构造函数、复制赋值运算符、移动赋值运算符和析构函数。你可以显式删除所有特殊成员函数。
派生类中的构造函数 派生类构造函数始终调用基类构造函数,因此,在完成任何额外任务之前,它可以依赖于完全构造的基类。调用基类构造函数进行派生,例如,如果 ClassA 派生自 ClassB,ClassB 派生自 ClassC,那么首先调用 ClassC 构造函数,然后调用 ClassB 构造函数,最后调用 ClassA 构造函数。
如果基类没有默认构造函数,则必须在派生类构造函数中提供基类构造函数参数:


具有多重继承的类的构造函数 如果类从多个基类派生,那么将按照派生类声明中列出的顺序调用基类构造函数:



构造函数中的虚函数 我们建议你谨慎调用构造函数中的虚函数。基类构造函数始终在派生类构造函数之前调用,因此基构造函数中调用的函数是基类版本,而非派生类版本。在下面的示例中,构造 DerivedClass 会导致执行 BaseClass 的 print_it() 实现早于 DerivedClass 构造函数导致执行 DerivedClass 的



构造函数和复合类 包含类类型成员的类称为“复合类”。创建复合类的类类型成员时,调用类自己的构造函数之前,先调用构造函数。当包含的类没有默认构造函数是,必须使用复合类构造函数中的初始化列表。在之前的 StorageBox 示例中,如果将 m_label 成员变量的类型更改为新的 Label 类,则必须调用基类构造函数,并且将 m_label


委托构造函数 委托构造函数调用同一类中的其他构造函数,完成部分初始化工作。在下面的示例中,派生类具有三个构造函数,第二个构造函数委托第一个,第三个构造函数委托第二个:





继承构造函数 (C++11) 派生类可以使用 using 声明从直接基类继承构造函数,如下面的示例所示:



using 语句可将来自基类的所有构造函数引入范围(除了签名与派生类中的构造函数相同的构造函数)。一般而言,当派生类未声明新数据成员或构造函数时,最好使用继承构造函数。
如果类型指定基类,则类模板可以从类型参数继承所有构造函数:


如果基类的构造函数具有相同签名,则派生类无法从多个基类继承。
声明构造函数的规则 构造函数与它的类的名称相同。可以声明任意数量的构造函数,这取决于重载函数的规则。

C++ 定义两种特殊的构造函数(默认构造函数和复制构造函数),如下表所述。

默认构造函数和复制构造函数

默认构造函数可在没有参数的情况下调用。但是,如果所有参数都有默认值,则可以用参数列表声明默认构造函数。同样,复制构造函数必须接受对相同类类型的引用的单一参数。可以提供多个参数,前提是所有后续参数都有默认值。
如果未提供任何构造函数,则编译器将尝试生成默认构造函数。如果未提供复制构造函数,则编译器将尝试生成一个。这些编译器生成的构造函数被视为公共成员函数。如果使用属于对象但不属于引用的第一个参数指定复制构造函数,则将生成错误。
编译器生成的默认构造函数将设置对象(如上文所述,初始化 vftables 和 vbtables),并调用基类和成员的默认构造函数,但是它不执行任何其他操作。仅当基类和成员构造函数存在、可访问并且无歧义时才会调用它们。
编译器生成的复制构造函数将设置新的对象,并对要复制的对象的内容按成员复制。如果基类或成员构造函数存在,则将调用它们;否则将执行按位复制。
如果类 type 的所有基类和成员类均具有接受 const 参数的复制构造函数,则编译器生成的复制构造函数将接受 const type& 类型的单个参数。否则,编译器生成的复制构造函数将接受 type& 类型的单个参数。
您可以使用构造函数初始化 const 或 volatile 对象,但是,构造函数本身不能声明为 const 或 volatile。构造函数的唯一合法存储类是 inline;将任何其他存储类修饰符(包括 __declspec 关键字)与构造函数一起使用将导致编译器错误。
stdcall 调用约定用于使用 __stdcall 关键字声明的静态成员函数和全局函数,且不使用变量参数列表。对非静态成员函数(如构造函数)使用 __stdcall 关键字时,编译器将使用 thiscall 调用约定。
基类的构造函数不由派生类继承。创建派生类类型的对象时,该对象将从基类组件开始进行构造;然后移到派生类组件。由于整个对象有一部分已初始化,因此编译器使用每个基类的构造函数(虚拟派生的情况除外,如初始化基类中所述)。
显式调用构造函数 可以在程序中显式调用构造函数来创建给定类型的对象。例如,若要创建描述某行末尾的两个 Point 对象,请编写以下代码:


创建类型 Point 的两个对象,将其传递给函数 DrawLine,并在表达式(函数调用)的末尾将其销毁。
在其中显式调用构造函数的另一个上下文正在进行初始化:


使用接受类型为 Point 的两个参数的构造函数来创建和初始化类型为 int 的对象。
通过显式调用构造函数创建的对象(如上面的两个示例)未进行命名,并且该对象具有在其中创建它们的表达式的生存期。 临时对象中更详细地讨论了这一点。
通常,从构造函数的内部调用所有成员函数是安全的,因为该对象在用户代码的第一行执行之前已完全设置(已初始化虚拟表等)。但是,在构造或析构期间,成员函数调用抽象基类的虚拟成员函数可能是不安全的。
构造函数可以调用虚函数。调用虚函数时,调用的函数将是为构造函数自己的类定义的函数(或从其基类继承)。以下示例演示从构造函数的内部调用虚函数时发生的情况:


}

更多“以下有关继承正确的是A: 构造函数和析构函数都能被继承B: 派生类是基类的组合C: 派生类对象除”相关的问题

以下有关继承的叙述正确的是()。

A.构造函数和析构函数都能被继承

B.派生类是基类的组合

C.派生类对象除了能访问自己的成员以外,不能访问基类中的所有成员

D.基类的公有成员一定能被派生类的对象访问

下列有关继承和派生的叙述中,正确的是()。

A.派生类构造函数可以被继承,派生类析构函数不能被继承

B.派生类析构函数可以被继承,派生类构造函数不能被继承

C.派生类构造函数的总参数表列中的参数,应当包括基类构造函数和子对象的参数表列中的参数

D.在执行派生类的构造函数时,调用基类的构造函数就可以对子对象初始化

下面叙述正确的是()。

A.派生类可以有自己的构造函数

B.派生类继承基类的构造函数

C.派生类继承基类的析构函数

D.派生类只能继承基类的一个构造函数

下列关于派生类的描述中,错误的是()

A.派生类不可以继承基类的构造函数

B.派生类不可以继承基类的拷贝构造函数

C.派生类不可以继承基类的虚函数

D.派生类不可以继承基类的析构函数

下列描述中,正确的是()。

A.派生类构造函数主要是对派生类新定义的成员变量进行初始化

B.派生类从基类继承的成员变量的初始化通过基类构造函数完成

C.派生类对象被销毁时会先调用基类析构函数

D.派生类对象被销毁时会先调用派生类析构函数

以下关于派生类说法中正确的是()

A.派生类继承了基类中除构造函数和析构函数外的全部成员

B.派生类继承了基类中全部成员

C.派生类只继承了基类中的Public成员

下列有关继承和派生的叙述中,正确的是()

A.派生类不能访问通过公有继承的基类的保护成员

B.派生类的对象可以访问基类的任何成员

C.如果基类有带形参构造函数,派生类可以不声明带形参的构造函数

D.基类构造函数和析构函数都不能够被继承

以下关于派生类说法中正确的是()。

A.派生类继承了基类中除构造函数和析构函数外的全部成员

B. 派生类继承了基类中全部成员

C. 派生类只继承了基类中的Public成员

派生类继承基类中除构造函数和析构函数以外的所有成员

下面描述中,正确的是()。

A.多重继承中,派生类对象被销毁时析构函数的调用顺序与派生类对象创建时构造函数的调用顺序相反

B.多重继承中,派生类对象被销毁时析构函数的调用顺序与派生类对象创建时构造函数的调用顺序一致

C.多重继承中创建派生类对象时各基类构造函数的调用顺序与基类构造函数在初始化列表中的出现顺序一致

D.多重继承中,如果派生类的初始化列表中没有某个基类构造函数的调用语句,则表示调用该基类的无参构造函数

}

我要回帖

更多关于 什么是虚函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信