类——构造函数之初始化列表

目录

前言

1. 再谈构造函数

1.1 构造函数体赋值

1.2 .1什么是初始化列表

1.2.2初始化列表的格式

1.2.3 关于初始化列表存在意义:

1.2.4初始化列表和成员声明缺省值

1.2.5初始化列表的顺序

小结



前言

        在我们已经基本了解了类的由来,和其六大基本默认函数后,此时我们再回过头来看,其中的构造函数。在前文已经说过构造函数的作用为初始化成员变量,但是真的就这么简单了吗?如果成员变量为常变量(const)那还能构造成功吗?在构造函数体内部,我们直接可以用类的成员变量来赋值,但是在调用构造函数前是没有这个类对象的,既然都没有这个类对象,哪前面我们直接用来赋值的类中的成员变量是哪里来的?

        下面将带来以上的解答与本人理解(若有错误欢迎指出)。


1. 再谈构造函数
 

1.1 构造函数体赋值


我们先来复习一下构造函数,如下:

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。

class Date
{
public:
Date(int year, int month, int day)
{
    _year = year;
    _month = month;
    _day = day;
}
private:
    int _year;
    int _month;
    int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量
的初始化
,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。(可能有些同学会说构造函数不就是初始化吗?这也不就是构造函数,为啥就不是初始化了呢?请续看下文)因为初始化只能初始化一次,而构造函数体内可以多次赋值。(初始化和赋值的区别)
 

1.2 .1什么是初始化列表

在托出初始化列表前为了我们更好的理解,它与构造函数的区别和关系,我们来一个列子作为对比

 见上左图:A的第一次为定义赋值,而第二次为的赋值就为二次赋值了。

见下图:我们发现我们在创建A2这个实例化对象时,调用了构造函数但在进入构造函数体前,这个A2居然已经创建(初始化)好了,虽然这是个随机数,那它是在哪里创建这个对象的呢?

此时初始化列表就说:没错就是我干的,是我来真正初始化类成员的,构造函数体内只是二次赋值罢了。

但在上面的图片中,我们明明没看见这个所谓的初始化列表那他在哪里呢?

1.2.2初始化列表的格式

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值或表达式。

我们以日期类来做列子:

lass Date
{
public:
 Date(int year, int month, int day)
    : _year(year)
    , _month(month)
    , _day(day)
{}
private:
    int _year;
    int _month;
    int _day;
};

1.2.3 关于初始化列表存在意义:

初始化列表不就是定义赋值罢了,我们在构造函数体内二次赋值,结果不是一样的吗?

!注意

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
        ①引用成员变量

引用变量为随机值,是一种很危险的行为(引用是由C语言的指针修改而来,本质上还是指向内存的地址


        ②const成员变量(整个生命周期只有在定义时可以赋值一次)

const修饰的变量,为常变量,具有常性(不可修改/二次赋值),只能在定义时赋值


        ③自定义类型成员(且该类没有默认构造函数时,如果有,可以不用,他会自动调用其自己的构造函数)

class A
{
public:
    A(int a)
    :_a(a)
    {}
private:
    int _a;
};
class B
{
public:
    B(int a, int ref)
    :_aobj(a)
    ,_ref(ref)
    ,_n(10)
    {}
private:
    A _aobj; // 没有默认构造函数
    int& _ref; // 引用
    const int _n; // const
};

1.2.4初始化列表和成员声明缺省值

前文中,我们还提到了,由于编译器对于内置类型的初始化不做处理(结果内置类型就变为了随机值),在C++11 中针对内置类型成员不初始化的缺陷,打了补丁,即:内置类型成员变量在
类中声明时可以给默认值。

如下图

 如果在成员变量处添加了默认值(缺省值),在初始化列表处又该如何运行的呢?

 我们发现这2个地方,若都有赋值的话,则会优先初始化列表的赋值,由此我们可以推出以下结论,成员变量处的默认值,是作用于初始化列表的,而不是用于构造函数体内的,且该值为初始化列表的缺省值(我们可以取一个形象一点的名字:初始化列表的缺省值——定义缺省值

1.2.5初始化列表的顺序

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后
次序无关。

(很离谱是吧,我也觉得,为了防止因顺序出现的BUG,成员变量的声明顺序最好和初始化列表的先后顺序一致

来一波热炒热卖吧!请看下题:

class A
{
public:
    A(int a)
    :_a1(a)
    ,_a2(_a1)
    {}
void Print() {
    cout<<_a1<<" "<<_a2<<endl;
}
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
} 
A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值

答案:D

让我康康有没有小可爱选了A的,有的话罚你在看一遍。
 


 

小结

        尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化,即在创建新的类对象时,都会调用构造函数,也都会经过初始化列表(就算你不写,编译器也会走)。

        尽量使用初始化列表初始化还有个好处,就是将初始化的步骤可以尽可能的放在一起,而构造函数体内就可以写其它的需求实现,比如访问或修改除了新类对象的其它变量等等,就可增加代码的可读性。

class Time
{
public:
    Time(int hour = 0)
    :_hour(hour)
    {
        cout << "Time()" << endl;
    }
private:
    int _hour;
};
class Date
{
public:
    Date(int day)
    {}
private:
    int _day;
    Time _t;
};
int main()
{
    Date d(1);
}

  • 34
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值