初始化VS赋值
一个数据在定义时就直接设置成某值,就叫“初始化”;而一个数据已经存在后才被设置成某值,叫“赋值”
int i0;//全局数据,会被自动初始化为0.
int i1; //构造方式1,若i1为全局数据,则初始化为0,若为局部数据,则不初始化。
int i2(); //构造方式2
cout << "i2(),构造方式会导致i2自动初始化为0,i2 = " << i2 << endl;
::a 两个冒号代表全局变量
全局数据有特权,如果程序员不为它写初始化的代码,编译器也会自动将它按照“填0”的方式进行初始化
全局整数被默认初始化成0,字符是‘\0’,指针是nullptr。如果是一个结构或类(struct/class)的全员对象,就是其成员都按以上规则进行初始化。
建议:不要依赖全局数据会自动初始化的特性,不管什么数据,只要逻辑上需要,就明确写上初始化代码。
不初始化:
//版本一:没有构造函数,将由编译器默认帮忙生成
truct Point1
{
int x, y;
};
接着定义一个Point变量pt。pt将占用一块内存,这块内存不会得到初始化,意思是原来里面有什么,现在就保留着什么,因此x和y值通常是乱乱的一个数。C++中,这叫“类的默认初始化”。
如何做到不初始化。
给一个类或结构的定义,但不为它提供构造函数,就可以做到不出事。给出默认构造函数,但又什么都不做,效果也是没有初始化:
//版本二:人工提供一个空的构造函数,其实还是什么也没做
struct Point2
{
int x, y;
Point2(){};
};
C++11更进一步,允许程序员明确在结构或类定义中指明这家伙的默认初始化工作内容,就是什么也不做,请编译器自行处理去吧。其方法是写上默认构造函数,不实现它,只是将它声明为“default(默认的)”,语法是:
//版本三:明确标示采用编译器的默认生成的空构造
struct Point3
{
int x,y;
Point3() = default;
};
构造式初始化:
我们还没有为Point设计初始化的规则,这是一个类的构造函数所应当做的事:
//版本四:人工提供的默认初始化,在构造函数体内对各成员初始化
struct Point4
{
Point4() //增加默认构造
{
x = y = 0; //相当于x=0,y=0
}
int x, y;
};
初始化规则有了,即当Point新对象产生,不管是在什么内存端,该坐标点将位于坐标轴的原点上。
......
Point pt1; //调用了默认构造
cout << pt1.x << " | " << pt1.y << enl;
通过后天努力,Point的栈对象现在和全局对象站到同一水平线上,实现了填零式的初始化。我们不能满足于此,最好是在构造时,可以指定目标位置。这时需要带入参的构造函数:
struct Point5 //版本三
{
Point5() //默认构造
{
x = y = 0;
}
//新增带入参的构造函数,定义Point对象时可指定x和y的初值。
Point5(int ax, int ay)
{
x = ax;
y = ay;
}
int x,y;
};
现在,定义Point对象时可指定x和y的初值:
Point pt2_1(0, 90); //栈对象
Point* pt2_2 = new Point(45, -125);//堆对象
“pt2_1”和“pt2_2”的初始化方式,就叫“构造式初始化”。
回头看pt1的构造,虽然没有写圆括号,但其实它仍然调用(默认的)构造函数,完整写法是:
Point pt1(); //完整写法带有“()”
以上是用户自定义的类或结构体类型。有意思的是内置类型,同样这两种写法:
int i1; //构造方式1
int i2(); //构造方式2
第一种写法我们很熟悉,i1若为全局数据,则初始化为0;若为局部数据,则不初始化。
第二种写法, i2固定被初始化为0。因此局部数据是否被初始化的选择权在程序员手上,要初始化,就写“int i();”,不想初始化就写“int i;”。
“int i();”是合法的,那么我们写“int i(99);”十有八成也合法,并且i将被初始化为99。
int i2(99); //构造自一个字面量
int i3(i2); //构造自另一个int变量
其中 i3 演示如何在构造时复制另一个同类变量的值。更多类型数据构造式初始化的例子:
char c('C');
double d(20.3);
float f(89.5F);
堆对象使用“构造式初始化”的例子:
int* pi_100 = new int(100);
char* pc = new char('C');
Point* ppt = new Point(45, 190);
Point* ppt2 = new Point(*ppt);
指针自身也可以使用构造式初始化,形成嵌套两层的构造式初始化语法,例如:
int* pi_100 (new int(100));
Point* ppt(new Point(45, 190));
这就是C++中的“构造式初始化”,对内置类型和用户自定义类型(struct/class等)有着一致语法的初始化方法,并且这里面隐约透漏出“面向对象”的思想:
一是让简单类型的数据,看起来也像是“对象”;二是强调“类型即封装”的思想,无论简单类型还是复杂类型。“构造式初始化”可以称为C++中最有格调的初始化方法。