1 引入构造函数
对于Date类,可以通过SetDate公有的方法给对象设置内容,但是如果每次创建对象都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
#if 0
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void PrintDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
//d._year = 2020;
d1.SetDate(2020, 10, 11);
d1.PrintDate();
Date d2;
d2.SetDate(2020, 10, 12);
d2.PrintDate();
Date d3;
d3.SetDate(2020, 10, 12);
d3.PrintDate();
Date d4(2020,10,13);
d4.PrintDate();
int a;
a = 10;
cout << a << endl;
int b = 10;
cout << b << endl;
int c(20);
cout << c << endl;
return 0;
}
#endif
2 什么是构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
//构造函数可以重载,并证明对象再声明周期内只调用一次
#if 0
class Date
{
public:
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
//证明对象再声明周期内只调用一次
cout << "Date(int,int,int):" << this << endl;
}
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void PrintDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1(2020, 10, 11);
d1.PrintDate();
// 创建对象时,编译器自动调用构造函数,给对象中设置一些初始值
Date d2;
d2.PrintDate();
//函数声明,而不是创建对象
Date d3();
}
int main()
{
TestDate();
return 0;
}
#endif
3 构造函数的特性
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
3.1 函数名与类名相同
3.2 无返回值
3.3 对象实例化时编译器自动调用对应的构造函数
3.4 构造函数可以重载
class Date
{
public:
// 1.无参构造函数
Date ()
{}
// 2.带参构造函数
Date (int year,int month,int day )
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1;// 调用无参构造函数
Date d2 (2015, 1, 1);// 调用带参的构造函数
// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
Date d3();
}
3.5 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
- 有显式定义构造函数
#if 0
// 用户没有显式定义任何构造函数---显式:用户有没有自己写
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void PrintDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
// 从概念:
// 目前日期类中没有显式定义构造函数,但是该类仍旧可以创建对象,当创建对象时,编译器需要自动调用构造函数
// 该构造函数哪里来的,就是编译器生成的默认无参构造函数
Date d;
return 0;
}
#endif
- 用户显式定义编译器将不再生成
#if 0
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
{
_hour = hour;
_minute = minute;
_second = second;
}
void PrintTime()
{
cout << _hour << ":" << _minute << ":" << _second << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
// 用户显式定义了带有参数的构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void PrintDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
// Date类中已经显式定义了带有三个参数的构造函数
// 验证:编译器还会不会生成无参的构造函数呢?
// 如果代码可以通过编译则编译器生成无参构造函数了
// 如果代码编译失败则编译没有生成
// 编译报错:error C2512: “Date”: 没有合适的默认构造函数可用
// 当类中显式定义了构造函数时,编译器不会再生成无参的默认构造函数了
Date d; // 创建d对象期间,编译器需要调用无参的构造函数
return 0;
}
#endif在这里插入代码片
3.6 无返回值无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个.注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
#if 0
class Date
{
public:
// 无参构造函数
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
// 全缺省构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void PrintDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 10, 11);
// 发现:即可以调用无参构造函数来创建d2,
// 也可以调用全缺省的构造函数来创建d2
// 产生了二义性,编译器就报错了
Date d2;
return 0;
}
#endif
3.7 对象实例化时编译器自动调用对应的构造函数编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数
/*
1. 当用户没有显式定义构造函数时,编译器会生成一个无参的默认构造函数
*/
#if 0
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
{
_hour = hour;
_minute = minute;
_second = second;
cout << "Time(int,int,int)" << endl;
}
void PrintTime()
{
cout << _hour << ":" << _minute << ":" << _second << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void PrintDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
Date d;
return 0;
}
#endif
3.8 成员变量的命名风格
class Date
{
public:
Date(int year)
{
// 这里的year到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
// 所以我们一般都建议这样
class Date
{
public:
Date(int year)
{
_year = year;
}
private:
int _year;
};
// 或者这样。
class Date
{
public:
Date(int year)
{
m_year = year;
}
private:
int m_year;
};
// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行。
往期链接:
cpp_8 类和对象上(四) — this遗留
cpp_7.1 类和对象上(三) — this
cpp_7类与对象上(二) — 类对象的存储方式
cpp_6.1类与对象上(一)— 类的引入
cpp_6 nullptr
cpp_5.2 auto关键字
cpp_5.1 内联函数
cpp_5 const修饰的变量和宏
cpp_4 引用
cpp_3 函数重载/传址调用/引用
cpp_2 输入输出/函数/重载
cpp_1 命名空间/输入输出