一. 类的默认成员函数
默认成员函数就是⽤⼾没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类,我 们不写的情况下编译器会默认⽣成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最 后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后还会增加两个默认成员函数, 移动构造和移动赋值,这个我们后⾯再讲解。默认成员函数很重要,也⽐较复杂,我们要从两个⽅⾯去学习:
• 第⼀:我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求。
• 第⼆:编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现,那么如何⾃⼰实现?
类的构造函数就是一个public类型,名字和类名相同的一个函数,只不过在实例化类对象的时候,会自动调用,不用我们手动调用了,下面我来写一个。
这就是我们的一个构造方法,里面有无参的,也有三个参数的,都可以调用,三个参数的调用形式就是如图所示了,但是没有参数的就不能Data d3()像这样去调用了,因为此时无法确定你调用的是无参构造还是创建的返回值类型为Data类型的函数了。
也不能弄成缺省参数的形式,如下图:
此时就会报错了,因为编译器不知道该调用哪一个构造函数了。
如果我们,没有写构造函数编译器会自动给我们生成一个,但是可能不会符合我们的预期。
它的结果此时就是随机值,并不符合我们的要求。
这些就是类和对象的一些基本入门的代码。
二.构造函数
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并 不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化 对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数⾃动调⽤的 特点就完美的替代的了Init。
构造函数的特点:
1. 函数名与类名相同。
2. ⽆返回值。 (返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
3. 对象实例化时系统会⾃动调⽤对应的构造函数。
4. 构造函数可以重载。
5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显 式定义编译器将不再⽣成。
6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成 函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫 默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调 ⽤的构造就叫默认构造。
7. 我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始 化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始 化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要⽤ 初始化列表才能解决,初始化列表,我们下个章节再细细讲解。
自定义类型就是我们自定义定义的一些类或者其他的东西。
我们上面讲了,初始化构造函数既然无法初始化我们想要的结果,那么它有什么用呢?
我们来看一下这个代码。此时我们在类a中定义了两个A1对象,此时构造函数的作用就会体现出来了。
此时我们发现a类调用了构造方法,初始化了a1和a2,虽然还是依赖于A1的构造函数吧。
但是如果没有默认构造方法就无法调用了,如下图:
此时我把A1的构造方法改了。
编译阶段并没有错误,因为此时只是声明吗,并不是真正的定义,当分配完内存后,才是定义。
此时我们就完成了定义,此时就报错了。
为什么说a的构造方法被删除了呢?
-
在类
a
中,有两个A1
类型的成员a1
和a2
。由于A1
类没有默认构造函数,而在类a
的定义中,没有为这两个成员提供合适的初始化方式(正确的方式是在类a
的构造函数初始化列表中初始化它们)。 -
当试图创建类
a
的对象(a a;
)时,编译器无法为a1
和a2
找到合适的默认构造函数来进行初始化。这就导致了一种好像类a
的构造函数(这里实际上编译器会为类a
生成默认构造函数,但由于成员初始化问题无法正常工作)“被删除” 或者无法正常使用的错觉。 -
实际上,类
a
的默认构造函数(由编译器生成)还是存在的,只是因为其中的成员初始化出现问题而导致编译错误。正确的做法是要么为A1
类添加默认构造函数,要么在类a
的构造函数初始化列表中显式地初始化a1
和a2
成员,这样才能让类a
的构造函数正常工作。-
你可以理解为,a在调用自己的初始化构造函数的时候,此时要初始化a1和a2吗,但是a1和a2这两个类中并没有合适的构造函数去初始化它们自己,因为a的构造函数初始化a1和a2时,也是要调用A1的构造方法的。
-
如果真的不好理解的话,你只需要记住我只能调用你的默认构造,没有默认构造就不能调用了。
-
-
-
看此时,发现main函数中的不报错了,因为可以找到初始化它们的构造函数了,但是类a中的为什么报错呢?简单说就是这里的a2为什么调用不到两个参数的构造方法呢?
在 C++ 中,对于类的数据成员初始化,在类定义内部直接像 A1 a2(1,2); 这样写是不合法的。这是因为类定义主要是在描述类的成员变量和成员函数的布局和接口,而不是用于初始化成员变量的具体值。编译器在处理类定义阶段,不会自动去调用非默认构造函数来初始化成员对象。它期望成员对象要么通过默认构造函数初始化(如果有默认构造函数),要么在类的构造函数的初始化列表中进行初始化。
就是只能调用自己默认的构造函数。
三.析构函数
析构函数与构造函数功能相反,析构函数不是完成对对象本⾝的销毁,⽐如局部对象是存在栈帧的, 函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会⾃动调⽤析构函数,完成对 象中资源的清理释放⼯作。析构函数的功能类⽐我们之前Stack实现的Destroy功能,⽽像Date没有 Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的。
析构函数的特点:
1. 析构函数名是在类名前加上字符 ~。
2. ⽆参数⽆返回值。 (这⾥跟构造类似,也不需要加void)
3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。
4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会 调⽤他的析构函数。
6. 还需要注意的是我们显⽰写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数。
这里的~Data就是析构函数了。这是析构函数,也是自动调用的,但是我们这里的内容都是在栈帧中的,并不需要释放,所以没啥用,如果是malloc申请地址了什么的,此时就需要我们手动释放内存了。
但是这种我们malloc的函数需要去释放内存了。析构函数不管在什么时候,都会被调用。
四.结束语
感谢大家的查看,希望可以帮助到大家,做的不是太好还请见谅,其中有什么不懂的可以留言询问,我都会一一回答。 感谢大家的一键三连。