目录
目录
一、构造函数
1.构造函数出现原因
2.定义
3.使用
4.构造函数调用顺序
5.构造函数的作用
二、类的组合
1.引出概念
三、类成员初始化的困惑——冒号语法
1.使用说明
2.注意事项
3.步骤
前言:
每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其他的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C++编译器会自动为A产生四个缺省函数:
- A(void);//缺省的构造函数
- A(const A &a);//缺省的拷贝构造函数
- ~A(void); //缺省的析构函数
- A & operate=(const A &a);//缺省的赋值函数
本文讲述构造函数的相关知识点,其余三个函数会在后面文章陆续发出。
- 举例桌子类Table,属性为长宽高,一个输出函数printf,一个设置函数set,在主函数内定义桌子类的对象t,输出该对象的属性,又定义了桌子属性类的对象t1,设置高为60,输出该对象的属性,如下代码所示:
#include
using namespace std; class Table { public: void Print(); void Set(); private: int m_length;//长 int m_width;//宽 int m_height;//高 }; void Table::Print() { cout << m_length << " " << m_width << " " << m_height << endl; } void Table::Set() { int m_length = 120;//长 int m_width = 40;//宽 int m_height = 80; } int main() { Table t;//定义对象,自动调用Table的构造函数 t.Print();//期待输出结果为120 40 80 } - 运行上述代码,输出第一个对象t的属性,期待输出合法的值:120 40 80。但结果出现下面所示的随机值:
- 解决方案:
- 构造函数是个特殊的成员函数函数,函数名和类名相同,无返回类型,可以带参数(可以重载)
- 在使用该类定义的对象时,发现如果程序员没有定义构造函数,则类会提供一个默认的构造函数,给类中的数据成员分配空间。如果写了,系统就不会再提供构造函数。上例所示运行结果出现了地址,开辟了空间就调用了构造函数,但是程序员没写,就说明了系统提供默认构造函数。
- 举例Table桌子类,与上面不同的是,设置构造函数及Table的函数名后带上了参数默认值(如果要带默认值,只能在参数后面初始化的时候写)。示例如下所示:
class Table { public: Table(int l = 120, int w = 40, int h = 80)//带默认值参数的构造函数 { m_length = l;//长 m_width = w;//宽 m_height = h;//高 } void Print() { cout << m_length << " " << m_width << " " << m_height << endl; } private: int m_length;//长 int m_width;//宽 int m_height;//高 }; int main() { Table t;//定义对象,自动调用Table的构造函数 t.Print(); //输出结果为120 40 80 Table t1(60); t1.Print(); //输出结果为60 40 80 }
- 此时运行结果就是合法值
- 说明:
- 上述main函数内定义了对象t,系统执行时自动调用Table的构造函数Table(int l=120,int w=40,int h=80),输出默认值 120 40 80
- 如果构造函数内无参Table(int l,int w,int h),那么在调用时会报错,因为程序员自己定义了构造函数,那么系统不会再提供默认值,而程序员自己写的构造函数又是无参的没有默认值,就没办法输出。
Table tt[5];//定义对象数组
- 定义对象数组,说明当前数组内有5个对象,调用了5次构造函数
Table *p;//指针变量
- 定义指针变量,并未定义对象,不会调用构造函数,而且p为指针,在栈内开辟了四个字节,不需要构造函数再为其开辟内存。
- 使用:
Table *p=&t;//指向t p->print();
- 遇到对象->自动调用当前类的构造函数,调用步骤:
- 1.传参
- 2.根据数据成员在类中的声明顺序开辟空间
- 3.执行构造函数的函数体
构造函数三作用:
注意没有开辟空间。
对象不可以调用构造函数,但是可以调用析构
析构函数没有重载,构造函数有
- 一个类的对象(cpu,mouse,keyboard)作为另一个类(computer)的数据成员出现,叫做类的组合
- 另外一个包含的关系是聚合
- 组合是强拥有的关系——整体和部分(cpu坏了conputer也会坏),聚合是弱拥有的关系(就像公司与员工的关系一样,员工离职公司也会照样存在)
- 以下面代码学生和日期类进行举例:
class Date//日期类 { public: Date(int y,int m,int d) { m_y=y;//年 m_m=m;//月 m_d=d;//日 } void Show() { cout<
代码解释:
定义了日期类包含年year月month日day的属性以及一个输出函数
定义了学生类包含编号num生日birthday的属性以及一个输出函数
在主函数内定义Date类的d对象并初始化
主函数内定义Student类的s对象并初始化
输出对象s的属性信息
- 问题:
- 编号1001和姓名lisi都可以按照流程传参输出,但是birthday的d传到Date birthday处后,birthday作为Date的对象,理应调用Date的构造函数来输出初始年月日信息,但是在Date未进行参数初始化,无法进行赋初值输出(上面说了,因为程序员写了构造函数后不论有没有初始化参数,系统都不会再调用自己的构造函数提供默认值)
- 解决方案:
- 2,使用冒泡语法解决
- birthday(d)的意思是把d的值赋给了birthday
- :后面的内容是初始化,大括号{}里的内容是赋初值,赋初值≠初始化
- 用冒泡语法赋值的,就不必再在构造函数内进行初始化。
- 是一个冒号:,两个冒号::是作用域。
- :后面只能用(),不能用赋值号=
- 在构造函数体内必须对参数赋初值(赋初值≠初始化)