未定义构造函数的默认值

3

Bjarne写道:
对于类型T,T()是默认值的符号表示,由默认构造函数定义。 如果我们不声明默认构造函数会发生什么?例如:

using namespace std;

class date{
    int n,m;
    public:
   int day(){return n;}
   int month(){return m;}
          };//no default constructor

int main()
{
     date any =date();
     cout<<any.month()<<endl;   
     cout<<any.day()<<endl;
return 0;

}

这个程序的输出结果每次运行都是0和0。我没有声明任何默认构造函数,那么为什么会存在默认值即0?
编辑-
    class date{
        int n,m;
        public:
        date (){
        m=1;}
       int day(){return n;}
       int month(){return m;}
     };

 int main()
  {
     date any =date();
     cout<<any.month()<<endl;   
     cout<<any.day()<<endl;
return 0;

}

阅读了答案后,我提供了一个默认构造函数,但现在n的值变成了垃圾值。根据答案,由于m超出了任何其他构造函数的范围,并且它是值初始化(如答案中所述),因此n应该为0。
3个回答

5
你的类的行为是明确定义的。
为什么行为是明确定义的? 规则是: 如果你没有提供无参构造函数,编译器会为你的程序生成一个以防需要使用。但有个例外: 如果你的程序定义了该类的任何构造函数,则编译器不会生成无参构造函数。
根据C++标准,对象可以通过以下3种方式进行初始化:
零初始化 默认初始化 值初始化
当类型名称或构造函数初始化器后面跟着()时,初始化是通过值初始化进行的。
因此,
date any =date();
              ^^^
Value Initializes表示对无名对象进行值初始化,然后将其复制到本地对象any中,同时:
date any;

将会是一个默认初始化。
值初始化将会为那些超出任何构造函数范围的成员提供一个零值初始值。在你的程序中,n和m都超出了任何构造函数的范围,因此会被初始化为0。
对于编辑后的问题的答案:
在你编辑后的情况下,你的类提供了一个无参构造函数`date()`,它可以(并应该)初始化成员变量n和m,但是这个构造函数没有初始化这两个成员变量,所以在这种情况下没有进行零初始化,并且对象中未初始化的成员变量具有不确定(任意)值,进一步复制到any对象中显示不确定的成员变量值。
对于标准粉丝:
对象初始化规则在C++03标准8.5/5中得到了很好地定义:
要将类型为T的对象进行零初始化意味着: - 如果T是标量类型(3.9),则将对象设置为0 (零)转换为T的值; - 如果T是非联合类类型,则每个非静态数据成员和每个基类子对象都被零初始化; - 如果T是联合类型,则对象的第一个命名数据成员被零初始化; - 如果T是数组类型,则每个元素都被零初始化; - 如果T是引用类型,则不执行初始化。
要将类型为T的对象进行默认初始化意味着: - 如果T是非POD类类型(clause 9),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则该初始化是非法的); - 如果T是数组类型,则每个元素都进行默认初始化; - 否则,对象被零初始化。
要将类型为T的对象进行值初始化意味着: - 如果T是具有用户声明构造函数(12.1)的类类型(clause 9),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则该初始化是非法的); - 如果T是没有用户声明构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都进行值初始化; - 如果T是数组类型,则每个元素都进行值初始化; - 否则,对象被零初始化。

1
使用 date any; 不会对其进行初始化。date any = date(); 会调用编译器生成的默认构造函数,该函数将为所有基类和成员调用默认构造函数。对于 int 类型,默认构造函数是 int() 。它将把 int 成员初始化为 0 。您可以自行查看 链接 - lapk
@AzzA:仅仅因为一个程序表现出可观察的行为并不意味着它是正确的。无论如何,我编辑了答案并添加了标准规范,解释了为什么它以这种方式工作。是的,我不介意编辑我的错误答案以更正它们。 - Alok Save
@AzzA:你能提供标准中说默认构造函数会初始化v-table的参考吗?这是一个笼统的说法,标准根本没有提到v-table,动态分派是一种实现细节。不要依赖和基于可观察行为来建立你的基础,未定义和未指定的行为可能会展示任何行为。 - Alok Save
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/7879/discussion-between-azza-and-als - lapk
@Als:接下来我提供一个默认构造函数,像这样date (){m=1;}。你刚才说的是“值初始化为那些超出任何构造函数范围的成员提供了零的初始值。在你的程序中,n和m超出了任何构造函数的范围,因此被初始化为0。”所以输出应该是n=0,m=1。但是n却得到了垃圾值。 - T.J.
显示剩余7条评论

4

因为编译器会在您的代码中为您生成一个默认构造函数,有时编译器会不情愿地这么做。

编辑:由于Als说这并没有回答问题,我来详细解释一下。当您使用date any = date();时,您调用了编译器生成的默认构造函数。该构造函数调用所有基类和数据成员的默认构造函数。对于您的数据成员int,默认构造函数是int(),它将值设置为0。这里是ideone.com上的代码

#include <iostream>

int main( void )
{
 int i = -123;

 i = int(); 

 std::cout << i << std::endl; 


 return( 0 );
}

程序输出:

0

@Als 是的,它确实有用。请看我对你回答的评论。 - lapk
接下来我提供一个默认构造函数,像这样:date (){m=1;}但是现在n得到了垃圾值。但它没有调用int()将n变为零。 - T.J.
@T.J. date::date() : n(), m(1) {} - lapk

0

来自C++草案标准(1996年12月工作文件):

如果类X没有用户声明的构造函数,则会隐式声明默认构造函数。隐式声明的默认构造函数是其类的内联公共成员。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接