理解构造函数的概念

时间:2014-08-14 04:16:56

标签: c++ constructor language-lawyer

我不能正式理解构造函数在C ++中的含义。我正在阅读3.8子句(对象生命周期,N3797),并遇到以下内容:

  

如果一个对象属于a,则称该对象具有非平凡的初始化   类或聚合类型,它或其成员之一由初始化   除了普通的默认构造函数之外的构造函数。

我想了解一般的初始化。我已阅读第8.5节,N3797。如果某个对象被初始化,是否会调用构造函数(可能是普通的默认值)?我的意思是每个初始化过程(甚至是零初始化)都意味着构造函数调用。如果您提供相应的标准参考,那将是件好事。

6 个答案:

答案 0 :(得分:3)

  

我不明白C ++中的构造函数意味着什么。

据我所知,该标准没有明确包含术语“构造函数”的定义。但是,§12.1/ 1说

  

构造函数没有名称。特殊的声明符语法用于声明或定义构造函数。   语法使用:

     
      
  • 一个可选的 decl-specifier-seq ,其中每个 decl-specifier 函数说明符constexpr
  •   
  • 构造函数的类名和
  •   
  • 参数列表
  •   
     

按此顺序。在这样的声明中,忽略构造函数类名称周围的可选括号。

因此,如果您根据此语法声明成员函数,则您声明的函数是构造函数。另外,

  

默认构造函数(12.1),复制构造函数和复制赋值运算符(12.8),移动构造函数   和移动赋值运算符(12.8),析构函数(12.4)是特殊的成员函数。 [注意:   实现将在程序执行时隐式声明某些类类型的这些成员函数   没有明确声明它们。如果它们使用得很多,那么实现将隐式定义它们(3.2)。    见12.1,12.4和12.8。 - 结束注释]程序不应定义隐式声明的特殊成员函数。

(§12/ 1)

所以你去了 - 每个类至少有三个构造函数被声明,无论是隐式还是显式;您还可以使用§12.1/ 1中的语法声明其他构造函数。这样声明的整个函数集构成了构造函数集。

  

如果初始化某个对象,是否会调用构造函数(可能是普通的默认值)?我的意思是每个初始化过程(甚至是零初始化)都意味着构造函数调用。

不,这是假的。例如,int没有构造函数。尽管与类类型对象的初始化相比,您可以使用类似语法初始化int

struct Foo {};
Foo f {}; // calls default constructor
int i {}; // sets the value of i to 0

此外,零初始化从不调用构造函数,但零初始化也不是对象初始化中的步骤。

如果用“object”表示“类类型的对象”,那么总是调用构造函数仍然不行,尽管总是声明三个构造函数,如上所述。有关值初始化的信息,请参见§8.5/ 7:

  

T类型的对象进行值初始化意味着:

     
      
  • 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),那么   调用T的默认构造函数(如果T没有可访问的默认值,则初始化格式不正确   构造函数);
  •   
  • 如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象   是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,那么构造函数是   调用。
  •   
  • 如果T是数组类型,则每个元素都是值初始化的;
  •   
  • 否则,该对象为零初始化。
  •   

因此,当非联合类类型的默认构造函数是微不足道的并且您正在初始化该类型的对象时,实际上不会调用构造函数。

答案 1 :(得分:0)

构造函数是特殊函数。它看起来像一个普通的功能。它没有返回类型(尽管有人说返回类型是类的对象),如void,int,char,double等。它与Class的名称相同。它在创建对象时自动运行。 在C ++中,您不需要新的运算符来初始化对象。只需声明它并设置其属性即可。即ClassA object1, object2;

例如

Class Player{
int jerseyNo;

//constructor
Player(){
cout<<"HELLO";
}
};

在主要内容中,您可以执行以下操作:

Player nabin;

您也可以声明析构函数。析构函数是类似于构造函数的特殊函数,但在对象被销毁时运行,即当对象移出范围时运行。

示例:

Class Player{
..
~Player(){
cout<<"Destructor ran";
}
..
};

P.S 构造函数和析构函数的执行顺序是相反的。 示例:

Player p1,p2,p3;

执行顺序

1. p1's constructor runs
2. p2's constructor runs
3. p3's constructor runs
4. p3's destructor runs
5. p2's destructor runs
6. p1's destructor runs

答案 2 :(得分:0)

类是类型:

  

一个类是一种类型。

     

§9[class]

但是,并非所有类型都是类。标准指的是§3.9中不是类类型(例如标量类型)的类型。

但是,只有类类型可以具有成员函数:

  

在类的定义中声明的函数(不包括用friend说明符声明的函数)称为该类的成员函数。

     

§9.3[class.mfct]

构造函数是成员函数。因此,可以存在没有构造函数的类型(即,不是类类型的类型)。因此,初始化不一定涉及调用构造函数,因为可以初始化非类类型(例如int)。

请注意,某些东西不必是类类型的“对象”:

  

对象是存储区域。

     

§1.8[intro.object]

因此,int虽然不是类类型,但却是“对象”。

答案 3 :(得分:0)

你问:

  

如果初始化某个对象,是否会调用构造函数(可能是普通的默认值)?

答案简短:不。

更长的答案:只为类类型的对象调用构造函数。对于其他类型的对象,没有构造函数,因此无法调用构造函数。

你说:

  

我的意思是每个初始化过程(甚至零初始化)都意味着构造函数调用。

类类型的对象可以通过调用构造函数来初始化,构造函数可以是显式的或隐式的。它们也可以通过其他初始化方法初始化。通过直接设置其占用的内存的初始值来初始化其他类型的对象。

您已经看到了关于初始化的标准草案第8.5节。

有关类构造函数的详细信息,请参阅 12.1构造函数部分。

有关类初始化的详细信息,请参见 12.6初始化

部分

答案 4 :(得分:0)

我认为您的答案可以在N.397的3.6.2和8.5中找到。

始终在创建对象时调用构造函数。但是对象的初始化是 多步骤过程。

我理解零初始化是独立的,并且作为对象初始化期间的第一步执行,构造函数(可能是普通的默认值)稍后调用

答案 5 :(得分:0)

基本上,无论何时在c ++中构造数据类型,它都可以通过两种方式之一实现。您可以调用构造函数,也可以基本上复制内存块。所以构造函数并不总是被称为。

在C ++中,有一个基元概念。整数,双精度,指针,字符,指针(对任何东西)都是原始的。基元是数据类型,但它们不是类。所有基元都可以安全地按位复制。创建或分配基元时,不会调用构造函数。所有这一切都会产生一些汇编代码,它们会复制一些代码。

课程有点复杂;答案是通常会调用构造函数,但并非总是如此。特别是,C ++ 11具有普通类的概念。普通类是满足几个条件的类:

  1. 他们使用所有“特殊”函数的默认值:构造函数,析构函数,移动,复制,赋值。
  2. 他们没有虚拟功能。
  3. 该类的所有非静态成员都是普通的类或原语。
  4. 就C ++ 11而言,满足此要求的任何类的对象都可以被视为数据块。当您创建这样的对象时,它的构造函数将不会被调用。如果你在堆栈上创建这样一个对象(没有new),那么它的析构函数也不会在程序终止时被调用,就像通常被调用一样。

    不要相信我的话。查看assembly generated for the code I wrote。你可以看到有两个类,一个是微不足道的,一个不是。非平凡的类在程序集中调用了它的构造函数,而普通的类则没有。