C++超详细讲解构造函数_第1页
C++超详细讲解构造函数_第2页
C++超详细讲解构造函数_第3页
C++超详细讲解构造函数_第4页
C++超详细讲解构造函数_第5页
已阅读5页,还剩13页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第C++超详细讲解构造函数目录类的6个默认成员函数构造函数特性编译器生成的默认构造函数成员变量的命名风格

类的6个默认成员函数

如果我们写了一个类,这个类我们只写了成员变量没有定义成员函数,那么这个类中就没有函数了吗?并不是的,在我们定义类时即使我们没有写任何成员函数,编译器会自动生成下面6个默认成员函数。

classS

public:

int_a;

这里就来详细介绍一下构造函数。

构造函数

使用C语言,我们用结构体创建一个变量时,变量的内容都是随机值,要想要能正确的操作变量中存储的数据,我们还需要调用对应的初始化函数,给成员变量赋一个合适的初值。那么C++呢,我们仍然使用这个方法来试试。

classDate

public:

voidSetDate(intyear,intmonth,intday)

_year=year;

_month=month;

_day=day;

voidDisplay()

cout_year"-"_month"-"_dayendl;

private:

int_year;

int_month;

int_day;

intmain()

Dated1,d2;

d1.SetDate(2025,5,1);//初始化

d1.Display();

d2.SetDate(2025,7,1);//初始化

d2.Display();

return0;

完全没问题的,毕竟C++完全兼容C嘛,不过这样子做未免有点麻烦,能不能做到我一创建好对象,对象的成员变量就是已经被初始化了而不是我们主动去调用呢?C++提供了一个特殊的函数:构造函数。

构造函数是一个特殊的成员函数,名字与类名相同,无返回值,每次使用类实例化对象时会自动调用,保证每个数据成员都有一个合适的初值,方便我们的后续使用,构造函数在对象的生命周期内只会被调用一次。

特性

虽然叫构造函数,但它的作用并不是构造一个对象(申请空间创建对象),而是初始化对象。

特征如下

函数名与类名相同;无返回值;对象实例化时编译器自动调用对应的构造函数;构造函数可以重载;

classDate

public:

Date()//无参的构造函数

Date(intyear,intmonth,intday)//带参的构造函数

_year=year;

_month=month;

_day=day;

voidDisplay()

cout_year"-"_month"-"_dayendl;

private:

int_year;

int_month;

int_day;

intmain()

Dated1;//调用无参的构造函数

Dated2(2001,7,28);//调用带参的构造函数

d1.Display();

d2.Display();

Dated3();//这种写法要不得,会被当做函数名为d3,无参、返回类型为Date的函数声明。

//调用无参的构造函数,一定不要加()否则编译器无法识别这是一个函数声明还是调用的无参构造。

return0;

如果编译器没有显式的定义构造函数,编译器会自动生成一个无参的默认构造函数,一旦我们显式定义,编译器就不再生成;

classDate

public:

voidDisplay()

cout_year"-"_month"-"_dayendl;

private:

int_year;

int_month;

int_day;

intmain()

Dated1;//同样能创建一个对象

d1.Display();

return0;

输出:

可以看到使用编译器生成的默认构造函数我们的日期仍然是随机值。

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

classDate

public:

Date()//无参的默认构造函数

Date(intyear=2001,intmonth=7,intday=28)//全缺省的默认构造函数

_year=year;

_month=month;

_day=day;

voidDisplay()

cout_year"-"_month"-"_dayendl;

private:

int_year;

int_month;

int_day;

intmain()

Dated1;//这里无法编译通过,因为调用不明确。

d1.Display();

return0;

一般我们使用全缺省的构造函数,既可以不传参用缺省值去初始化对象,也可以显式地去调用并用实参初始化对象。

编译器生成的默认构造函数

欸好像这货没什么用啊,我刚刚使用这个自动生成的,我的对象的初值还是随机值啊,看起来就像这构造函数什么事都没有做。

其实C++把类型分成内置类型(基本类型)和自定义类型。

内置类型就是语法已经定义好的类型:如int/char,自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。

不过这涉及到我们还没学过的知识初始化列表,后面会讲。

classTime

public:

Time(inthour=0,intminute=0,intsecond=0)

cout"Time(inthour=0,intminute=0,intsecond=0)"endl;

_hour=hour;

_minute=minute;

_second=second;

private:

int_hour;

int_minute;

int_second;

classDate

public:

//使用编译器默认的构造函数

voidDisplay()

cout_year"-"_month"-"_dayendl;

private:

//内置类型

int_year;

int_month;

int_day;

//自定义类型

Time_t;

intmain()

Dated1;

d1.Display();

return0;

输出:

Time(inthour=0,intminute=0,intsecond=0)

-858993460858993460858993460

事实上编译器生成的默认构造函数并不是什么都没有做,而是只处理了成员变量中的自定义类型,而没有去初始化内置类型,调用自定义类型成员的构造函数就是在初始化列表做的,下面会详细讲。

成员变量的命名风格

看看下面这种成员命名方式有什么缺陷?

classTime

public:

Time(inthour=0,intminute=0,intsecond=0)

cout"Time(inthour=0,intminute=0,intsecond=0)"endl;

hour=hour;

minute=minute;

second=second;

private:

inthour;

intminute;

intsecond;

简直太难看了,hour=hour这是什么操作???到底哪个hour是成员变量,让人去猜吗,代码实在丑陋,因此我们在对成员变量命名时,为了初始化对象不会因为发生命名冲突,又能一眼看出来哪个形参是对应初始化哪个成员变量的,我们通常在对成员变量命名方式统一成成员变量名前加上下划线_。

classTime

public:

Time(inthour=0,intminute=0,intsecond=0)

cout"Time(inthour=0,intminute=0,intsecond=0)"endl;

_hour=hour;

_minute=minute;

_second=second;

private:

int_hour;

int_minute;

int_second;

当然不止是这样,适合自己的才是最好的,不过一般都是采用加一个前缀或者加上一个后缀的方式来命名成员变量。

这里很容易弄混两个概念,在此强调一下:

默认构造函数是不需要参数的构造函数,有以下三种:

编译器生成的;显式定义的无参的构造函数;显式定义的全缺省的构造函数;

默认成员函数是我们如果不写,编译器会自动生成的函数;

构造函数的初始化列表

前面说了,在实例化一个类的对象时会自动去调用类的构造函数进行对象的初始化操作,那在C++中一个自定义类型的过程可分为两个过程:

为对象分配内存;对成员变量赋值;函数体内赋值;

那我们想想如果成员变量是具有常属性的,那么是不是2过程就无法生效了?那么对于那些具有常性的变量我们以前是怎么定义的呢?初始化操作可以完成这个问题。

初始化是什么?变量在定义的同时为它设定一个初值,例如:引用必须初始化

inta=10;,这就是一个典型的初始化操作,而我们要谈的初始化列表也是相似的,那么这种初始化操作有什么与众不同的呢?

答案是:对于那些一旦有初值就不能再被赋值的变量,初始化列表的作用就体现出来了,例如被const修饰的变量,或者是引用,这些都是具有常属性的,因此就需要在它们创建的过程中就给它们一个初值,保证其可以被正常初始化。

classS

public:

S(inti=0,intj=0)

_i=i;

_j=j;

private:

constint_i;//const修饰i具有常属性

int_j;

intmain()

Ss;

return0;

报错:

S::_i:必须初始化常量限定类型的对象

errorC2166:左值指定const对象

即在编译器看来,这个对象包括对象中的成员变量在进入构造函数的函数体后,就都已经初始化完成了,因此const修饰的变量i就无法再被赋值了。

既然初始化列表是在创建变量阶段对变量进行的初始化,因此就可以使用初始化列表处理给那些无法修改的变量。

初始化列表格式如下:

classS

public:

S(inti=0,intj=0)

:_i(i),

_j(j)

private:

constint_i;

int_j;

intmain()

Ss;

return0;

程序正常运行

同时呢,建议能使用初始化列表就使用,尽量不在构造函数的函数体中为成员变量再赋值,养成好的习惯。

下面分析编译器生成的默认构造函数到底做了什么事情

classTime

public:

Time(inthour=0,intminute=0,intsecond=0)

:_hour(hour),

_minute(minute),

_second(second)

cout"Time(inthour=0,intminute=0,intsecond=0)"endl;

voidDisPlay()

cout_hour"-"_minute"-"_secondendl;

private:

int_hour;

int_minute;

int_second;

classDate

public:

voidDisplay()

cout"Date:";

cout_year"-"_month"-"_dayendl;

cout"Time:";

_t.DisPlay();

private:

int_year;

int_month;

int_day;

Time_t;

intmain()

Dated1;

d1.Display();

return0;

输出:

可以看到,Date类中的内置类型都未被初始化,而对于自定义类型是去调用了其默认构造函数并且初始化成功了的。

这里要记住一定是调用的自定义成员的默认构造函数,因为编译器生成的Date的默认构造函数调用Time的构造时默认是不传参的,毕竟它也不知道传什么嘛。

如果我们把Time构造函数的缺省值去掉,那么Time就没有默认构造函数,那么创建Date对象时就无法调用Time的默认构造函数,就出错了。如下

classTime

public:

Time(inthour,intminute,intsecond)

:_hour(hour),

_minute(minute),

_second(second)

cout"Time(inthour=1,intminute=1,intsecond=1)"endl;

voidDisPlay()

cout_hour"-"_minute"-"_secondendl;

private:

int_hour;

int_minute;

int_second;

classDate

public:

voidDisplay()

cout"Date:";

cout_year"-"_month"-"_dayendl;

cout"Time:";

_t.DisPlay();

private:

int_year;

int_month;

int_day;

Time_t;

intmain()

Dated1;

d1.Display();

return0;

报错:

message:Date::Date(void):由于数据成员Date::_t不具备相应的默认构造函数

那我们现在已经知道了编译器生成的默认构造函数它能做什么了,接下来我们显式地去定义一个构造函数。

classTime

public:

Time(inthour=0,intminute=0,intsecond=0)

:_hour(hour),

_minute(minute),

_second(second)

cout"Time(inthour=0,intminute=0,intsecond=0)"endl;

voidDisPlay()

cout_hour"-"_minute"-"_secondendl;

private:

int_hour;

int_minute;

int_second;

classDate

public:

Date()

_year(),//调用了int的默认构造函数,并且会给初始化为0

_month(),

_day(),

_t()//调用了Time的默认构造函数,这里不写也会自动调用

cout"Date()"endl;

voidDisplay()

cout"Date:";

cout_year"-"_month"-"_dayendl;

cout"Time:";

_t.DisPlay();

private:

int_year;

int_month;

int_day;

Time_t;

intmain()

Dated1;

d1.Display();

return0;

我们为Date定义一个无参的默认构造函数,在初始化列表我不止处理了内置类型还处理了自定义类型。

在C++中支持这样一种定义变量的方法:

inta;//a是随机值

intb(1);

int();//创建匿名变量,调用默认构造

这和自定义类型的定义是一样的格式,这是为了让内置类型也能按照自定义类型的方式去定义和初始化,看起来是去调用了int的构造函数。

这样,我们在初始化列表中调用了初始化了内置类型和自定义类型,Date的内置类型也都被初始化为0了,这也印证了编译器生成的默认构造函数并没有去调用int的默认构造,而只调用了自定义的默认构造。

注意:就算我们显式的定义了构造函数,如果在初始化列表中不显式的调用Time的构造函数,那么编译器也会默认去调用它的默认构造(创建自定义类型成员变量时就一定会调用),而我们一旦显式的去调用了,那么走我们的调用。

C++中有这样一个特性,编译器能帮你做的,就算你不做它会自动帮你完成,而你一旦做了他就会按照你的方式去完成。

具体什么意思呢?看代码:

classTime

public:

Time(inthour=0,intminute=0,intsecond=0)

:_hour(hour),

_minute(minute),

_second

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论