定义一个n大小的类数组
发布时间:2025-06-24 18:58:28 作者:北方职教升学中心 阅读量:983
定义一个n大小的类数组。
加上const。
然后当天数!>0时,向上一个月借位,天数+=上个月的天数(注意是上个月的,因为当月的天数已经被减过了)。
Date Date::operator-(intday)const{Date tmp=*this;tmp -=day;returntmp;}
1.6复用对比
1.7日期类==日期类
判断两个日期类很简单。所以加上const可以防止我们的程序不小心篡改(原本不应该修改却不小心修改会报错),同时也让我们的的成员函数传参更宽泛,const成员也可以调用。
可能很多同学都会觉得选A。非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。那放在类外面我们如何访问类的成员变量呢?那我们就可以加一个友元函数声明。
classA{// 友元声明friendvoidfunc(constA&aa,constB&bb);private:int_a1 =1;int_a2 =2;};voidfunc(constA&aa,constB&bb){cout <<aa._a1 <<endl;cout <<bb._b1 <<endl;}
例如这里func是A的好朋友,func就可以访问A的成员。我们只需要判断是否他们的年月日都相等即可。const 修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为const Date* const this。
二.取地址运算符重载
2.1const成员函数
定义
将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。每次日期++就用一个变量记录天数即可。
classA{private:staticint_k;int_h =1;public:classB// B默认就是A的友元{public:voidfoo(constA&a){cout <<_k <<endl;//OKcout <<a._h <<endl;//OK}private:int_b;};};intA::_k =1;intmain(){cout <<sizeof(A)<<endl;A::B b;A aa;b.foo(aa);}
所以刚刚OJ的代码也可以改成内部类
classSolution{// 内部类classSum{public:Sum(){_ret +=_i;++_i;}};staticint_i;staticint_ret;public:intSum_Solution(intn){// 变⻓数组Sum arr[n];return_ret;}
- 默认友元
内部类默认是外部类的友元类。Date Date::operator+(intday)const{Date tmp=*this;tmp +=day;returntmp;}
1.4日期类-=整数
这里思路和我们前面实现+的思路一样。让tmp进行+=的逻辑这样改变就不会日期类。
1.1声明和定义分离
我们想让.h放函数的声明。话不多说,咱们进入正题!向大厂冲锋!
一.日期类的实现
为了加深我们前面对类和对象的理解。
先比较年。我们向当天数-day。我们需要返回istrem或ostrem对象。所以我们可以用一个类数组来构造这个条件。让月份=12然后年份–。都是通过借位的思想。但是这道题可以用我们的静态成员解决。因为匿名对象声明周期只在当前行。
不写加const。则向下月借位。 访问限定符
静态成员也是类的成员,受public、多个友元
⼀个函数可以是多个类的友元函数。private访问限定符的限制。我们对日期类拷贝一份tmp。
所以初始化列表能写尽写,因为就算你不写他也会走初始化列表。直接返回false.boolDate::operator<(constDate&d)const{if(_year <d._year)//比较年{returntrue;}elseif(_year ==d._year)//年相等{if(_month <d._month)//判断月{returntrue;}elseif(_month ==d._month)//月相等{return_day <d._day;//判断天}}returnfalse;//年不相等}
1.9比较复用
剩下的我们通过前面实现的<和==复用即可。并且编译器调用时会调用最匹配的。
匿名对象让我们写代码更方便。
Date(int&x,intyear =1,intmonth =1,intday =1):_year(year),_month(month),_day(day),a(1),_ref(x),_ptr((int*)malloc(size(int)//成员定义{if(_ptr ==nullptr){perror(malloc fail!);}}
例如有个指针,我们malloc以后需要检查是否失败。
使用
有时提供了便利。但实际不是这样的。所以还需要写两份。因为不论是const成员还是非const成员都可以调用。
boolDate::operator==(constDate&d)const{if(_year ==d._year &&_month ==d._month &&_day ==d._day){returntrue;}else{returnfalse;}}
1.8日期类<日期类
判断两个日期类<。B的所有成员函数都可以访问A的成员变量。
声明
外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。四.类型转换
classA{public://构造函数explicit就不再⽀持隐式类型转换// explicit A(int a1)A(inta1):_a1(a1){}//explicit A(int a1, int a2)A(inta1,inta2):_a1(a1),_a2(a2){}voidPrint(){cout <<_a1 <<" "<<_a2 <<endl;}private:int_a1 =1;int_a2 =2;};intmain(){// 1构造⼀个A的临时对象,再⽤这个临时对象拷⻉构造aa3//编译器遇到连续构造+拷⻉构造->优化为直接构造A aa1 =1;aa1.Print();constA&aa2 =1;// C++11之后才⽀持多参数转化A aa3 ={2,2};return0;}
- 隐式类型转化
C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。.cpp放函数的定义。没有默认构造的类类型也必须在初始化列表初始化,因为他必须手动传参调用构造函数。因为初始化列表初始化的顺序是声明的顺序。那就必须在函数体内检查。
intGetMonthDay(intyear,intmonth){assert(month >0&&month <13);staticintmonthDayArray[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};if(month ==2&&((year %4==0&&year %100!=0)||(year %400==0))){return29;}returnmonthDayArray[month];}Date&Date::operator+=(intday){_day +=day;while(_day >GetMonthDay(_year,_month)){_day -=GetMonthDay(_year,_month);_month++;if(_month ==13){_year++;_month =1;}}return*this;}
1.3日期类+整数
现在我们实现日期类+整数,也就是不改变日期类。
istream&operator>>(istream&in,Date&d){cout<<"请依次输入年月日:"<<endl;in >>d._year>>d._month >>d._day;returnin;}ostream&operator<<(ostream&out,constDate&d){out <<d._year <<"年"<<d._month<<"月"<<d._day<<"日";returnout;}
为了实现连续输入或输出。
大概形式如下:
Date(int&x,intyear =1,intmonth =1,intday =1):_year(year),_month(month),_day(day),_t(12),_ref(x),_n(1){// error C2512: “Time”: 没有合适的默认构造函数可⽤// error C2530 : “Date::_ref” : 必须初始化引⽤// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象 }
定义的位置
每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。否则就会变成多次定义。2.2取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们⽤了,不需要去显示实现。月<返回true。如果12借位则年份+1,月份改为1即可。直接返回即可。
初始化顺序
初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。
这里报错是为啥?
因为隐式类型转化生成是临时对象,临时对象具有常性。因为他们两个是默认成员函数。- 隐式类型转化
classTime{public:Time(inthour=0):_hour(hour){cout <<"Time()"<<endl;}private:int_hour;};classDate{public:Date(int&x,intyear =1,intmonth =1,intday =1):_year(year),_month(month),_day(day),a(1),_ref(x)//成员定义{// error C2512: “Time”: 没有合适的默认构造函数可⽤// error C2530 : “Date::_ref” : 必须初始化引⽤// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象}voidPrint()const{cout <<_year <<"-"<<_month <<"-"<<_day <<endl;}private://声明int_year;int_month;int_day;intmain();int&_ref;constinta;Time _t;};
引用成员变量,const成员变量必须在初始化列表初始化。
声明位置
友元函数可以在类定义的任何地方声明,不受类访问限定符限制。然后每次构造把当前的值累加即可。大家自己好好消化。
intDate::operator-(constDate&d)const{intsum =0;intflag =1;Date max,min;max =*this;min =d;if(*this<d)//假设法{swap(max,min);flag =-1;}while(min!=max){sum++;min++;}sum *=flag;returnsum;}
1.12日期类IO
因为类的成员函数默认*this抢占了第一个位置。建议声明顺序和初始化列表顺序保持⼀致。
这道题主要修饰构造出循环这个条件。
#pragmaonce#include<iostream>usingnamespacestd;#include<assert.h>classDate{friendistream&operator>>(istream&in,Date&d);friendostream&operator<<(ostream&out,constDate&d);//友元声明public:Date(intyear ,intmonth ,intday );voidPrint();// ĬinlineintGetMonthDay(intyear,intmonth){assert(month >0&&month <13);staticintmonthDayArray[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};if(month ==2&&((year %4==0&&year %100!=0)||(year %400==0))){return29;}returnmonthDayArray[month];}intGetYear();intGetMonth();intGetDay();booloperator<(constDate&d)const;booloperator<=(constDate&d)const;booloperator>(constDate&d)const;booloperator>=(constDate&d)const;booloperator==(constDate&d)const;booloperator!=(constDate&d)const;boolCheckDate();Date operator+(intday)const;Date&operator+=(intday);Date operator++(int);//后置Date&operator++();//前置Date operator--(int);//后置Date&operator--();//前置Date&operator-=(intday);Date operator-(intday)const;intoperator-(constDate&d)const;private:int_year;int_month;int_day;};istream&operator>>(istream&in,Date&d);ostream&operator<<(ostream&out,constDate&d);
我们日期类主要实现他的比较和日期类之间的相互运算。 ==返回比较天。所以istrem或ostrem必须用引用。除非⼀些很特殊的场景,比如我们不想让别⼈取到当前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。
那初始化列表和函数体内赋值可以混着用吗?
可以,因为有些场景必须混着用。
八.匿名对象
- 定义
用类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的类型对象名(实参)定义出来的叫有名对象。如果是1月的话,借去年12月的天数。
后言
这就是类和对象的深入理解。
- explicit
如果不想发生隐式类型转化, 构造函数前面加explicit就不再支持隐式类型转换。我们只需要定义两个静态成员。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
可以看到未打印1之前,A就析构了。
我们的思路就是直接让小的天数一直相加,直到相加到小的日期和大的日期相等即可。那我们可以复用日期类+=整数的逻辑。
这里我们可以验证一下。
那隐式类型转化有什么用?classStack{public:voidPush(constA&aa){}private:inttop;};intmain(){Stack s;A aa =1;s.Push(aa);s.Push(1);return0;}
可以发现隐式类型转化可以让我们写代码更加方便简洁。我们现在来试着手搓一个日期类。初始化是按照内存中的顺序初始化。访问权限
静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。但是const成员调用返回的是const this*。天数减去当月天数之和,以此类推知道天数不超过当月的最大天数位置。和我们平时的写法不一样。【C++笔记】类和对象的深入理解(三)
🔥个人主页:大白的编程日记
🔥专栏:C++笔记
文章目录
- 【C++笔记】类和对象的深入理解(三)
- 前言
- 一.日期类的实现
- 1.1声明和定义分离
- 1.2日期类+=整数
- 1.3日期类+整数
- 1.4日期类-=整数
- 1.5日期类-日期
- 1.6复用对比
- 1.7日期类==日期类
- 1.8日期类<日期类
- 1.9比较复用
- 1.10前置和后置
- 1.11日期类-日期类
- 1.12日期类IO
- 二.取地址运算符重载
- 2.1const成员函数
- 2.2取地址运算符重载
- 三.再探构造函数
- 四.类型转换
- 五.static成员
- 六.友元
- 七.内部类
- 八.匿名对象
- 后言
前言
哈喽,各位小伙伴大家好!上期我们讲了类和对象的更深入的内容。
总结
尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。按理说我们只写const成员函数即可。
==继续比较月。
- 友元类
友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员classA{// 友元声明friendclassB;private:int_a1 =1;int_a2 =2;};classB{public:voidfunc1(constA&aa){cout <<aa._a1 <<endl;cout <<_b1 <<endl;}voidfunc2(constA&aa){cout <<aa._a2 <<endl;cout <<_b2 <<endl;}private:int_b1 =3;int_b2 =4;};intmain(){A aa;B bb;bb.func1(aa);bb.func1(aa);return0;}
B是A的友元类。
- 封装
内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。对于没有显示在初始化列表初始化的⾃定义类型成员会调⽤这个成员类型的默认构造函数,如果没有默认构造会编译错误。传递性
友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
- 缺省值
C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。Date Date::operator++(int){Date tmp =*this;*this+=1;returntmp;}Date&Date::operator++(){*this+=1;return*this;}Date Date::operator--(int){Date tmp=*this;*this-=1;returntmp;}Date&Date::operator--(){*this-=1;return*this;}
1.11日期类-日期类
两个日期类相减得出他们之间相差的天数。
定义初始化
引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
静态成员函数
用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。Date&Date::operator-=(intday){_day -=day;while(_day <=0){if(_month ==1){_month =12;_day +=GetMonthDay(--_year,_month);}else{_day +=GetMonthDay(_year,--_month);}}return*this;}
1.5日期类-日期
日期类-日期我们就复用-=即可。为了不修改日期类。所以我们把日期类的输出和输出放在类外面。
三.再探构造函数
- 初始化列表
之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有⼀种方式,就是初始化列表,初始化列表的使用方式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后面跟⼀个放在括号中的初始值或表达式。classSum{public:Sum(){_ret +=_i;++_i;}staticintGetRet(){return_ret;}private:staticint_i;staticint_ret;};intSum::_i =1;intSum::_ret =0;classSolution{public:intSum_Solution(intn){//变⻓数组Sum arr[n];returnSum::GetRet();}};
- 练习二
C c;intmain(){A a;B b;staticD d;return0;}
六.友元
- 定义
友元提供了⼀种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到⼀个类的里面。因为初始化列表是定义的地方所以,只能初始化一次。那就会调用n次构造。今天讲了很多内容。所以要加上const引用。protected、因为他们必须在定义的地方初始化。
大家看一下这道题选什么。
同时注意流对象不支持拷贝。返回日期类+=整数的结果。private://声明int_year=1;int_month=1;int_day=1;int*_ptr=((int*)malloc(sizeof(int)));
在声明的位置给一个缺省值。如果你没有给缺省值,对于没有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。否则说明日期比当月的所有日期都大。
<返回true。感谢各位的耐心垂阅!咱们下期见!拜拜~
七.内部类
- 定义
如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。1.2日期类+=整数
这里我们的思路是先将整数加到天数上。
练习一
题目:求1+2+3…+n这道题目把常规的方法都限制了。
指定类域
突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量和静态成员函数。
五.static成员
- 静态成员变量
用static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化。boolDate::operator!=(constDate&d)const{return!(*this==d);}boolDate::operator<=(constDate&d)const{return*this<d ||*this==d;}boolDate::operator>(constDate&d)const{return!(*this<d)&&!(*this==d);}boolDate::operator>=(constDate&d)const{return!(*this<d);}
1.10前置和后置
前置我们拷贝一份*this,在复用+=或-=即可。今天我们就给类和对象收尾。
const修饰
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。因为权限可以缩小。- 共享
静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。缺省值
静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。声明是顺序是他们在内存存放的顺序。
单向性
友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
剩下的情况都是false的情况。所以能加尽加。
- 生命周期
匿名对象的生命周期只在当前⼀行,⼀般临时定义⼀个对象当前用⼀下即可,就可以定义匿名对象。然后如果天数不超过当月天数。classA{// 友元声明friendvoidfunc(constA&aa,constB&bb);private:int_a1 =1;int_a2 =2;};classB{// 友元声明friendvoidfunc(constA&aa,constB&bb);private:int_b1 =3;int_b2 =4;};
func既是A的友元又是B的友元。
classDate{public:Date*operator&(){returnthis;// return nullptr;}constDate*operator&()const{returnthis;// return nullptr;}private:int_year;// 年int_month;// ⽉int_day;// ⽇};
一般这两个成员函数都不需要我们显示的写。内部类是⼀个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
同时非const成员也可以调用const成员函数。