本节主要来说说类与对象这个模块吧,类的关键词就是 class;前边接触到的C语言,是面向过程的一门语言注重的就是过程;而C++是基于面向对象的,关注的是对象,这也就意味着,从今天开始要注重一些C++语言的要点了,接下来一起学习吧!
一、类的定义(结构形式)
(一)首先来了解一下类的结构形式:
class A{
//类体:由成员函数和成员变量组成
}; //类结束的分好不可以省略
如上述代码所展示的,定义了一个 A 类,A 就是类名,class 为定义类的关键词。
类的定义形式:
class A{
public:
void show(){}
protected:
void Ket() {}
private:
int a;
char b;
};
(二)类的访问限定符
1、public:公有
使用public 修饰的成员/成员函数 在类外可以直接访问
2、protected :保护
private :私有
使用protected / private 修饰的成员在类外不可以直接被访问
3、class 类的默认访问权限是 private ,struct 结构体为public
(三)类的特点
C++是一门面向对象的语言,面向对象有三大特性:封装、继承、多态
**封装:**是指将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现的细节,仅对外公开接口来和对象进行交互;封装本质上是一种管理。
类的作用域:
在类外定义成员,需要使用作用域解析符:: 来指明成员所属类域
class A{
public:
void Show();
private:
int a;
char b;
int c;
};
//此处说明Show 是A 类的
void A::Show()
{
cout<<a<<b<<b<<endl;
}
(四)类的成员变量/成员函数:
class A1{
public:
void Show(){} //类的成员函数
private:
int _a; //类的成员变量
};
class A2{
public:
void Show(){} //仅有成员函数
};
class A3{}; //空类 ,这里要注意空类的大小不为0
类的大小:
空类的大小一般为1;
类的大小,实际上就是类中“成员变量”之和,但是要注意内存对齐
对齐数=编译器默认的一个对齐数与该成员大小的较小值
(五)类中this 指针
类中默认存在一个指针 this ,其类型是 *const;
只能在“成员函数”内部使用;
this 指针本质上是一个成员函数的形参,是一个隐含的指针形参,一般情况由编译器自动传递,不许用户设置。
注意看这两处代码:
class A{
public:
void Show()
{
cout<<Show()<<endl;
}
};
class B{
public:
void Show(B* this)
{
cout<<Show<<endl;
}
};
这两段代码看起来不太一样(有参无参),实质上这两段代码实现的功能一模一样,第一个代码中间 *this 是隐含的形参,因此写与不写都可以。
二、类的成员函数
(一)构造函数
首先定义一个Date 日期类:
class Date{
public:
Date (){} //表示的是一个构造函数
private: //定义三个成员:年、月、日
int _year;
int _month;
int _day;
};
构造函数时类中一个特殊的成员函数,它具有一下特性:
1.函数名与类名相同----Date;
2.无返回值,可以有参可以无参;
3.对象实例化时编译器会自动调用相应的构造函数;
4.构造函数可以重载;
5、如果在一个类中用户并没有定义构造函数,则系统会自动调用一个默认的构造函数(无参),但一旦用户自己定义了构造函数,系统将不会默认生成。
构造函数的分类:无参构造、有参构造
(二)析构函数
析构函数与构造函数的功能正好相反,其表示形式是在构造函数函数名前加 ~
class Date {
public:
Date() //无参构造
{}
~Date() //析构函数
{}
private:
int _year;
int _month;
int _day;
};
析构函数的特性:
1.析构函数在对象在销毁时会自动调用,完成资源清理;
2.析构函数在类名之前加上 ~;
3.无参无返回值;
4.一个类有且仅有一个析构函数,若未显示定义析构函数,系统会自动生成默认的析构函数;
5.在对象生命周期结束时,系统自动调用析构函数
(三)拷贝构造函数
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(效率高),在用已存在的类类型对象创建新对象时由编译器自动调用。
class Date{
public:
Date(int year=1900,int month=1,int day=1){
_year=year; //全缺省构造函数
_month=month;
_day=day;
}
Date(const Date& d){ //拷贝构造函数
_year=d._year;
_month=d._month;
_day=d._day;
}
private:
int _year;
int _month;
int _day;
};
构造函数也同样具有些特性:
1.拷贝构造函数是构造函数的一个重载形式------形参需为 const 类名&;
2.拷贝构造函数只能有一个形参 : const 类名 &;
3.若用户未定义,系统会默认生成一个拷贝构造函数;
(四)内部类
在一个类中定义一个新的类:
class A{
public:
class B{ //在A 类中间定义了一个B 类
public:
void Show(){}
private:
int b;
};
private:
int a;
};
另外要注意:
class Time { // 定义一个时间类----时分秒
public:
Time() {
_hour = 17;
_minute = 01;
_second = 50;
}
/*Time(int hour = 17, int minute = 02, int second = 30) {
_hour = hour;
_minute = minute;
_second = second;
}*/
private:
int _hour;
int _minute;
int _second;
};
class Date { //定义一个日期类 Date---年月日
public:
Date()
{
_year = 2020;
_month = 10;
_day = 24;
}
private:
int _year;
int _month;
int _day;
Time t;
};
void test()
{
Date d1;
}
int main()
{
test();
return 0;
}
(五)操作符的重载
返回值类型 operator 操作符(参数列表)
注意:
1.不能进行重载的运算符:
.* 、:: 、sizeof 、?: 、.
2、要注意区分前置++/-- 与后置++/–
接下来主要以日期类 Date 来进行演示:
//首先,需要定义一个数组来存放每个月份所对应的天数:
int GetDate(int year, int month) { //获取相应月份天数
static int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = days[month];
if (month == 2) {
//判断闰年
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
day += 1;
}
}
return day;
}
这里需要说明一下:
闰年,能够整除4但不能整除100,但是可以整除400
1.赋值运算符的重载:
Date& operator=(const Date& d) {
if (&d != this) {
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
在进行赋值运算符重载时候,需要判读是否是给自己赋值,若是直接返回,若不是在进行赋值操作
此处出现了前边提到过的this 指针,有兴趣的童鞋们下去可以自己试试哦。
2.+=运算符的重载
一个日期加上一个数(天数)再将加之后的结果返回,我们可以使用 & 引用类型来提高运算的效率。
在者,一个给定日期加上一个对应的天数要考虑到所加天数的正负—加上一个正数先直接相加;加上一个负数等于减去一个正数,直接调用-= 操作
同理,也需要考虑到月份/年份的溢出问题。
Date& operator+=(int day) {
if (day < 0) { //加一个负数 = 减一个正数
return *this -= -day;
}
_day += day; // 加法要考虑月份天数的进位问题
while (_day > GetDate(_year, _month)) {
_month++; //判断是否溢出
if (_month == 13) {
_year++;
_month = 1;
}
}
return *this;
}
3.-=运算符重载
+= 需要考虑进位问题
同理思考方式,-= 就需要考虑借位问题
Date&operator-=(int day) {
if (day < 0) { // 减去一个负数 = 加上一个正数
return *this += -day;
}
_day -= day;
while (_day < 0) { //考虑是否借位
_month--;
if (_month == 0) {
--_year;
_month = 12;
}
_day += GetDate(_year, _month);
}
return *this;
}
4.==重载
判断日期相等---------》年、月、日 三者分别对应相等
bool operator==(const Date&d) { // 判等
return _year == d._year && _month == d._month && _day == d._day;
}
5.>=重载
bool operator >=(const Date&d) {
//return (operator==(d) || operator>(d));
if (_year >= d._year) {
return true;
}
else if (_year == d._year) {
if (_month >= d._month) {
return true;
}
else if (_month == d._month) {
_day >= d._day;
}
}
return false;
}
要实现相应的<= 、<、>、!= 等等重载可以按照上例给出的>= 来进行书写,可以按照上例格式改符号,同样也可以灵活使用!(取反)操作来实现,童鞋们可以私下自己联系联系呀,最后我将给出本次的日期类相关操作代码,有跟我一起写代码的可以对比下,顺便看看我的代码存在的不足 :
代码模块:
class Date {
public:
int GetDate(int year, int month) { //获取相应月份天数
static int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = days[month];
if (month == 2) {
//判断闰年
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
day += 1;
}
}
return day;
}
Date(int year = 2020, int month = 10, int day = 1) { //全缺省构造
_year = year;
_month = month;
_day = day;
}
//Date(const Date& d) { //拷贝构造
// _year = d._year;
// _month = d._month;
// _day = d._day;
//}
~Date() { //析构函数
cout << "~Date" << endl;
_year = 0;
_month = 0;
_day = 0;
}
//赋值运算符重载
Date& operator=(const Date& d) {
if (&d != this) {
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
//运算符重载
Date& operator+=(int day) {
if (day < 0) { //加一个负数 = 减一个正数
return *this -= -day;
}
_day += day; // 加法要考虑月份天数的进位问题
while (_day > GetDate(_year, _month)) {
_month++;
if (_month == 13) {
_year++;
_month = 1;
}
}
return *this;
}
Date&operator-=(int day) {
if (day < 0) { // 减去一个负数 = 加上一个正数
return *this += -day;
}
_day -= day;
while (_day < 0) {
_month--;
if (_month == 0) {
--_year;
_month = 12;
}
_day += GetDate(_year, _month);
}
return *this;
}
Date operator+() {
return *this += 1;
}
Date operator-() {
return *this -= 1;
}
Date& operator++(){ //前置++ 例:++i
return *this += 1;
}
Date operator++(int) { //后置++ 例:i++
Date tmp = *this;
*this += 1;
return tmp;
}
Date& operator--() { //前置-- 例:--i
return *this -= 1;
}
Date operator--(int) { //后置-- 例:i--
Date tmp = *this;
*this -= 1;
return tmp;
}
bool operator==(const Date&d) { // 判等
return _year == d._year && _month == d._month && _day == d._day;
}
bool operator!=(const Date&d) { //不等于
//return !operator==(d);
return (_year != _year) || (_month != d._month) || (_day != d._day);
}
bool operator >=(const Date&d) {
//return (operator==(d) || operator>(d));
if (_year >= d._year) {
return true;
}
else if (_year == d._year) {
if (_month >= d._month) {
return true;
}
else if (_month == d._month) {
_day >= d._day;
}
}
return false;
}
bool operator >(const Date&d) {
if (_year > d._year) {
return true;
}
else if (_year == d._year) {
if (_month > d._month) {
return true;
}
else if (_month == d._month) {
_day > d._day;
}
}
return false;
}
bool operator<(const Date&d) {
return !operator>=(d);
}
bool operator<=(const Date&d) {
//return !operator>(d);
return (operator<(d) || operator==(d));
}
void PrintDate() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//void test()
//{
// Date d1(2020, 10, 24);
// Date d2 = d1;
// Date d3(d1);
//}
void test()
{
Date d1(2020, 10, 12);
d1 += 10;
d1.PrintDate();
d1 -= 20;
d1.PrintDate();
Date d2(2019, 10, 2);
Date d3(2020, 10, 1);
Date d4(2020, 10, 12);
bool ret = d2 > d3;
ret = d4 <= d2;
ret = d3 >= d4;
}
int main()
{
test();
return 0;
}
运行解释:
定义 d1 时调用相应的构造函数
此时this --> d1
所有变量构造完成:
当所定义的 Date 类型的变量构造成功(调用拷贝构造函数)之后会自动调用析构函数,要注意析构函数的调用顺序与构造函数刚好相反---------------先构造的后析构
以上就是我本次想要分享的一些东西,希望感兴趣的童鞋可以自己练习练习,有些调试过程我没有完全展示出来,大家可以自己下去分步调试一下看看调用顺序,有问题的可以留言给我呀~~~~
biubiubiu~~
(博客内容全为小白原创,有问题的及时留言给我!)