十三、类的定义和实例化
1、类的语法形式
struct/class 类名:继承表{
访问控制限定符:
类名(形参表):初始化表{} //构造函数
~类名(void){} //析构函数
返回类型 函数名(形参表)[const]{} //成员函数
数据类型 变量名; //成员变量
}
2、访问控制限定符
public:公有成员,在类的外部和内部都可以访问的成员;
private:私有成员,只能在类的内部访问的成员;
protected:保护成员(后面讲)
eg:
struct/class {
eee; //struct默认公有成员,class默认私有成员
public:
aaa; //公有成员
private:
bbb; //私有成员
public:
ccc;
ddd;
};
3、构造函数(Constructor)
class 类名{
类名(形参表){} //构造函数
}
1)函数名和类名相同,没有返回类型;
2)构造函数在对象被创建时自动被调用;
3)构造函数主要负责初始化对象,即初始化成员变量;
4、对象的创建和销毁
1)在栈区创建单个对象
类名 对象(构造实参表);
类名 对象 = 类名(构造实参表);
注意:
如果构造函数没有参数,类名 对象;
或类名 对象 = 类名();
2)在栈区创建对象数组
类名 对象数组[元素个数] = {类名(构造实参表), … };
3)在堆区创建单个对象
创建:
类名* 对象指针 = new 类名(构造实参表);
销毁:
delete 对象指针
4)在堆区创建对象数组
创建:
类名* 对象指针 = new 类名[元素个数]{类名(构造实参表), ...}
销毁:
delete[] 对象指针;
练习:实现一个电子时钟,用构造函数接收当前系统时间,可以以秒为单位运行;
class Clock{
public:
Clock(time_t t){ //time_t = long int;
tm* local = localtime(&t);
m_hour = local->tm_hour;
...
}
void run(void){
while(1){
//打印当前时间
printf("\r%02d:%02d:%02d", m_hour, m_min, m_sec);
// 刷新标准输出缓冲区
fflush(stdout);
//计时加1
//sleep(1)
}
}
private:
int m_hour;
int m_min;
int m_sec;
};
int main(void)
{
//time(NULL):获取当前系统的时间
Clock clock(time(NULL));
clock.run();
}
5、多文件编程
1)类的声明部分放在xx.h文件中;
2)类的实现部分放在xx.cpp源文件中;
3)使用该类的代码一般会在其它的文件中;
练习:将Clock类拆分成多文件形式;
十四、构造函数和初始化表
1、构造函数可以重载也可以带有缺省参数
2、缺省构造函数
如果一个类没有定义任何构造函数,编译器会提供一个缺省构造函数(无参构造函数);
1)对于基本类型成员不做初始化;
2)对于类 类型成员变量,会自动调用相应类的无参构造函数来初始化;
注意:
如果定义了构造函数,无论是否有参数,编译器都不会提供缺省构造函数了;
3、类型转换构造函数(单参构造函数)
class 目标类型{
目标类型(源类型 src){...}
};
// 可以实现源类型到目标类型的隐式转换
注意:
使用explicit关键字修饰该构造函数,可以强制这种通过构造函数实现的类型转换必须显示的完成。
4、拷贝构造函数(重难点)
1)用一个已经存在的对象构造同类型的副本对象,会调用拷贝构造函数;
语法:
类名(const 类型& that){//完成成员的拷贝}
eg:
class A{
public:
A(const A& that){...}
};
A a1; // 无参方式构造a1
A a2(a1); // 调用拷贝构造函数来构造a2
A a2 = a1; // 和上面等价