C++类成员默认初始值

有时候咱们会不给C++类成员变量赋初始值,或是由于忘记在构造函数中指定(C++11能够写在类内),或是以为没有必要写。然而,由于以为编译器会把变量赋成0而不写是错误的。本文经过C++标准来解释这个问题。数组

本文基于N3337(C++11草案)标准。数据结构

关于没有初始化器的对象,在8.5-11中有说起:函数

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value.debug

没有初始化器的对象会被默认初始化;没有初始化的自动(局部变量)或动态存储期限(new出来的)对象的值是未定的。指针

这里涉及到了两种“无初始化”的概念,没有初始化器与没有初始化,注意区分。8.5-6对默认初始化(default-initialize)的定义是:调试

To default-initialize an object of type T means:code

  • if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);orm

  • if T is an array type, each element is default-initialized;对象

  • otherwise, no initialization is performed.递归

默认初始化T类型的对象是指:

  • 若是T是(可能有constvolatile的)类类型,T的默认构造函数会被调用(若是没有可用的,初始化就是非法的)(默认构造函数可能会对成员执行默认初始化);

  • 若是T是数组类型,每一个元素被默认初始化(一样是递归的默认初始化);

  • 不然,不初始化。

从这些标准中的条款,能够得出结论:自动或动态存储期限的非类类型对象,不管是不是数组或是否有const修饰,若是不指定初始值,它的值就是未定的。

而整数类型、指针类型等都属于非类类型,若是咱们但愿这些类型的成员变量有肯定的初始值,即便是看起来默认的0,也要本身写上初始化

----------------分割线----------------

我在这个问题上栽过大跟头,这就是我要写这篇文章的缘由。

那是一个单片机的C++程序,涉及到一个二重指针数组,类型是一个定义了虚函数的基类,经过指针调用虚函数。数据结构并不复杂,逻辑确定不会出错,可是程序跑飞了。

一开始以为是初始化的问题,就把全部全局变量换成单例模式建立,无果。而后加了许多调试语句,把问题定位到了解引用上。想了想指针解引用确定不会出问题,就以为问题在虚函数调用过程当中的函数指针解引用上。把指针调用(动态绑定)换成对象调用(静态绑定),果真程序就正常了。但函数指针解引用本质上是修改PC(程序计数器)寄存器,这种编译器搞定的事情我也插不了手。换了新版本的编译器,也没有解决。

后面的探索就奇怪了起来。我发现给PCB加一个100uF的电容可让程序在烧写后正常运行,但在从新上电后,一旦程序去调用那虚函数,单片机就会复位;若是把业务逻辑改简单一点,有助于从新上电后正常工做,但没有本质上解决问题。用了一些猥琐操做后,我发现不是单片机复位而是程序回到起始处。这多是未注册而触发的中断致使的,但就算给全部中断都指定了空函数,也仍是没有解决。

百思不得其解,调试了一成天都没有成功。

几个月后再来看这个中断的项目,从新读了一遍代码,发现了这个初始值的问题,终于解决了。后面就很顺了。

如今看来这个debug的过程一开始方向挺对的,后面的探索就慢慢差到十万八千里外去了。

烧写正常运行、上电不正常的奇怪现象也能够解释了。单片机内置复位电路会在上电时给寄存器赋初值,但内存中的数据仍是随机的。程序烧写先后内存中的数据被保留,以前的程序不知怎么把那一块内存初始化好了,烧写后就能够正常工做;而上电后那一块内存里都是随机值,对随机数进行4次解引用(二重指针2次,虚函数调用2次),程序早就不知道跑哪里去了。多是寻到合法地址空间之外去了,因为某些保护机制的存在,程序就回到了起始处。