小学生学C++编程语法知识(C++类初始化详解(一))


C++类初始化详解(一):

很多同学第一次学习类(class)时,会觉得:

“我创建一个对象,不就是给变量赋值吗?为什么还要学习构造函数、初始化列表?”


其实,类初始化是C++面向对象最重要的基础之一

今天我们解决几个核心问题:

  1. 一个类的成员变量有哪些初始化方式?

  2. 构造函数到底什么时候执行?

  3. 成员变量初始化顺序是什么?

  4. 为什么成员初始化列表更快?

  5. 哪些情况必须使用初始化列表?


一、先理解:什么叫初始化?

假设我们设计一个学生类:

class Student { public: string name; int age; };

创建对象:

Student s;

现在:

s +-------------+ | name | | age | +-------------+

但是:

name是什么? age是多少?

不知道。

所以我们需要给成员变量一个初始值。


这个过程叫:

初始化(Initialization)


二、类成员初始化的三种方式

C++中常见有三种方式。


方式1:创建对象后赋值

最简单:

class Student { public: string name; int age; }; int main() { Student s; s.name="Tom"; s.age=10; }

过程:

第一步:

创建对象:

Student s;

产生:

s +-------+ |name ? | |age ? | +-------+

第二步:

赋值:

s.name="Tom"; s.age=10;

变成:

+---------+ |Tom | |10 | +---------+

这种方法的问题:

对象已经出生了,然后再修改。


类似:

小孩出生:

宝宝出生 ↓ 再告诉他名字 ↓ 再告诉他年龄

效率低。


方式2:构造函数内部赋值

例如:

class Student { public: string name; int age; Student() { name="Tom"; age=10; } };

使用:

Student s;

执行:

进入构造函数 name="Tom" age=10

结果:

Student对象 name Tom age 10

看起来很好。

但是这里隐藏了一个问题。


三、重点:成员变量其实已经初始化了一次

例如:

class Student { public: string name; Student() { name="Tom"; } };

很多学生认为:

执行:

Student s;

过程:

name产生 ↓ name="Tom"

实际上不是。

真实过程:

第一步: string name创建 第二步: name调用默认构造函数 第三步: name="Tom"

也就是说:

发生了:

初始化一次 + 赋值一次

例如:

string:

第一次:

name=""

然后:

name="Tom"

实际上:

空字符串 ↓ 赋值Tom

多了一步。


四、方式3:成员初始化列表(推荐)

写法:

class Student { public: string name; int age; Student() : name("Tom"), age(10) { } };

注意:

冒号:

:

后面叫:

成员初始化列表


执行过程:

直接:

创建name 同时初始化为Tom

创建:

name="Tom"

一步完成。


所以:

构造函数赋值:

创建 ↓ 默认初始化 ↓ 赋值

初始化列表:

创建 ↓ 直接初始化

少了一步。


五、用生活例子理解

假设你买手机。

方法1:

买一个空手机:

手机出厂 ↓ 打开 ↓ 安装系统 ↓ 设置名字

方法2:

厂家直接:

生产手机时 直接装好系统 写好名字

当然第二种更快。


六、构造函数执行顺序(非常重要)

很多考试喜欢考。

例如:

class A { public: int x; int y; A() { cout<<"构造函数"<<endl; } };

创建:

A a;

执行:

1. 分配空间 2. 初始化成员变量 3. 执行构造函数 4. 对象创建完成

重点:

成员变量初始化发生在构造函数代码执行之前。


七、成员初始化顺序是谁决定的?

很多学生认为:

A() :x(10),y(20) { }

按照:

x 然后y

执行。

对吗?

不一定!

真正规则:

按成员变量在类中声明的顺序初始化。


例如:

class A { public: int x; int y; A() :y(20),x(10) { } };

很多人认为:

y先初始化 x后初始化

错误!

实际:

x先初始化 y后初始化

因为:

类里面:

int x; int y;

顺序决定。


记忆:

初始化列表顺序,看列表;
真正执行顺序,看成员声明。


八、为什么初始化列表更快?

我们比较:

方法1

class Student { public: string name; Student() { name="Tom"; } };

过程:

创建name ↓ 调用string默认构造 ↓ name="Tom"

两步。


方法2

Student() :name("Tom") { }

过程:

直接调用string有参构造 创建Tom

一步。


所以:

初始化列表:

减少:

临时对象 额外赋值 资源复制

效率更高。


九、哪些成员必须使用初始化列表?

这是重点。


1. const成员变量

例如:

class Student { public: const int age; Student() { age=10; } };

错误!

为什么?

因为:

const变量:

出生以后不能修改

但是:

age=10

属于修改。


必须:

Student() :age(10) { }

2. 引用成员变量

例如:

class Test { int &x; };

引用必须绑定对象。

不能:

x=10;

必须:

Test(int a) :x(a) { }

3. 没有默认构造函数的对象成员

例如:

class Phone { public: Phone(int price) { } };

没有:

Phone()

那么:

下面错误:

class Person { Phone phone; Person() { } };

因为:

创建Person时:

必须先创建phone。

但是:

phone不知道怎么创建。


解决:

Person() :phone(1000) { }

十、多个成员变量初始化

例如:

class Student { string name; int age; double score; public: Student(string n,int a,double s) :name(n),age(a),score(s) { } };

创建:

Student s("Tom",10,99.5);

过程:

name="Tom" age=10 score=99.5

十一、父类和成员对象初始化顺序(进阶)

学习继承会遇到。

对象出生:

顺序:

1. 父类初始化 2. 成员对象初始化 3. 自己成员初始化 4. 构造函数主体

例如:

class Child:public Parent { };

创建Child:

Parent出生 ↓ Child成员出生 ↓ Child构造函数

十二、析构函数的顺序(对应)

出生:

父类 ↓ 成员 ↓ 自己

死亡:

反过来:

自己析构 ↓ 成员析构 ↓ 父类析构

十三、小学生记忆口诀

构造函数:

对象出生自动调用 先成员 后自己

初始化列表:

冒号后面写初始化 比函数里面赋值快 const引用必须用它

成员初始化顺序:

不要看列表排哪里 只看类里声明序

十四、竞赛常见考点总结

知识点是否常考
构造函数自动执行⭐⭐⭐⭐
初始化列表语法⭐⭐⭐⭐
const成员必须初始化列表⭐⭐⭐⭐⭐
引用成员必须初始化列表⭐⭐⭐⭐⭐
成员初始化顺序⭐⭐⭐⭐⭐
初始化列表效率原因⭐⭐⭐

给学生的一句话总结:

构造函数负责“对象出生时做什么”,成员初始化列表负责“对象出生时是什么样”。

真正高级的C++程序员,会尽量让对象一出生就是完整状态,而不是出生以后再修改它。