类和对象下

目录

1. 赋值运算符重载

1.1 运算符重载

1.2 赋值运算符重载

1.3 日期类的实现

1.4 const修饰成员变量 

2. 构造函数之初始化列表

 3. 类型转换

3. static成员 

4. 友元

5. 内部类

6. 日期差


1. 赋值运算符重载

1.1 运算符重载

  • 当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。
  • 运算符重载是具有特殊名字的函数,他的名字是由operator和后面要定义的运算符共同构成。(operator加运算符构造函数名)和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
  • 重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。
  • 如果一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。
  • 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持一致。
  • 不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
  • .*  ::  sizeof  ?:  . 注意以上5个运算符不能重载。(选择题里面常考,大家要记一下)
  • 重载操作符至少有一个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int operator+(int x, int y)
  • 一个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义,但是重载operator+就没有意义。(一个类重载哪些操作符是看需要,看重载有没有价值和意义)
  • 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
  • 重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第一个形参位置,第一个形参位置是左侧运算对象,调用时就变成了对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream发到第一个形参位置就可以了,第二个形参位置当类类型对象。
#include<iostream>
using namespace std;// 编译报错:“operator +”必须⾄少有⼀个类类型的形参 
int operator+(int x, int y)
{return x - y;
}class A
{
public:void func(){cout << "A::func()" << endl;}
};//将指向A类中的无参无返回值的函数指针取个别名叫做PF
typedef void(A::* PF)(); //成员函数指针类型 int main()
{// C++规定成员函数要加&才能取到函数指针 PF pf = &A::func;A obj;//定义ob类对象temp // 对象调⽤成员函数指针时,使⽤.*运算符 (obj.*pf)();return 0;
}
#include<iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//这里把成员变量公有化,是为了让全局函数operator==中的对象可以使用成员变量// 重载为全局的⾯临对象访问私有成员变量的问题 // 有⼏种⽅法可以解决: // 1、成员放公有  // 2、Date提供getxxx函数 // 3、友元函数 // 4、重载为成员函数(一般使用这种方法)int _year;int _month;int _day;
};//这里用const修饰是因为怕在比较相等的过程中,把对象的值给修改了
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year &&d1._month == d2._month &&d1._day == d2._day;
}int main()
{Date d1(2024, 11, 30);Date d2(2024, 11, 31);//运算符重载可以显示调用operator==(d1, d2);//直接写,转换调用,编译器会转换成operator==(d1, d2);d1 == d2;return 0;
}

重载成成员函数

#include<iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}bool operator==(const Date d2){return _year == d2._year &&_month == d2._month &&_day == d2._day;}//前置加加Date& operator++(){cout << "前置加加" << endl;//...return *this;}//后置加加Date operator++(int){Date tmp = *this;cout << "后置加加" << endl;//...return tmp;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 11, 30);Date d2(2024, 11, 31);// 运算符重载函数可以显⽰调⽤ d1.operator==(d2);// 编译器会转换成 d1.operator==(d2); d1 == d2;// 编译器会转换成 d1.operator++(); ++d1;// 编译器会转换成 d1.operator++(0); d1++;return 0;
}

 如果全局也有operator==,在调用过程中默认还是使用类里面的成员函数operator==

1.2 赋值运算符重载

赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于一个对象拷贝初始化给另一个要创建的对象。

赋值运算符重载不能跟运算符重载一样,在类和全局都可以写,赋值运算符重载全局写不了

赋值运算符重载的特点:

  • 赋值运算符重载是一个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成 const当前类类型引用,否则会传值传参会有拷贝
  • 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。
  • 没有显式实现时,编译器会自动生成一个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数。
  • 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调⽤Stack的赋值运算符重载, 也不需要我们显⽰实现MyQueue的赋值运算符重载。这里还有一个小技巧,如果⼀个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要。
  • #include<iostream>
    using namespace std;class Date
    {
    public://构造函数Date(int year = 2024, int month = 11, int day = 30){_year = year;_month = month;_day = day;}//拷贝构造Date(const Date& d){cout << "Date(const Date& d)" << endl;_year = d._year;_month = d._month;_day = d._day;}//判断对象是否不相等bool operator!=(const Date& d){return !(_year == d._year &&_month == d._month &&_day == d._day);}//赋值拷贝构造//传引用返回减少拷贝Date& operator=(const Date& d){//要检测自己给自己赋值的情况//防止自己给自己赋值if (*this != d){_year = d._year;_month = d._month;_day = d._day;}// d1 = d2表达式返回的对象应该是d1,也就是*thisreturn *this;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
    private:int _year;int _month;int _day;
    };int main()
    {Date d1(2024, 11, 30);Date d2(d1);Date d3 = d1;Date d4;d4 = d1;//需要注意这里是拷贝构造,不是赋值//请牢牢记住赋值运算符重载完成两个已经存在的对象之间的拷贝赋值//而拷贝构造用一个对象拷贝初始化另一个要创建的对象Date d5 = d1;return 0;
    }

    对上述代码进行补充说明

     

    我们不难发现,void operator=(const Date& d)无返回值也可以完成赋值操作,那为什么还要使用Date呢?

    原因是:要支持连续赋值

    因为返回值是void,所以连续赋值就会报错。

    那我们看看连续赋值过程中的返回值是什么?

    下面说的func函数只是随便举的一个函数例子

    总结一下:返回对象是一个局部对象或临时对象,出了当前func函数作用域就析构销毁了,那么就不能用引用返回,用引用返回是存在风险的。

    出了作用域返回对象还在没有析构,那就可以使用引用返回,减少拷贝

    a、返回对象生命周期到了,会析构,传值返回

    b、返回对象生命周期没到,不会析构,传引用返回


    1.3 日期类的实现

有人问:函数重载和运算符重载有什么区别?

函数重载:可以让函数名相同,参数不同的函数存在

运算符重载:让自定义类型可以使用运算符,并且控制运算符的行为,增强可读性

多个运算符重载可以构造函数重载(前置加加和后置加加就是一个最好的例子)

#include <iostream>
using namespace std;class Date
{friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 2024, int month = 11, int day = 26){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//Date* const thisDate& operator=(const Date& d){if (*this != d){_year = d._year;_month = d._month;_day = d._day;}//这里返回*this是为了可以支持连续赋值return *this;}bool operator<(const Date& d) const;bool operator<=(const Date& d)const;bool operator>(const Date& d)const;bool operator>=(const Date& d)const;bool operator==(const Date& d)const;bool operator!=(const Date& d)const;Date& operator+=(int day);Date operator+(int day)const;Date& operator-=(int day);Date operator-(int day)const;Date& operator++();Date operator++(int);int operator-(const Date& d)const;//判断输入的日期是否是违法的bool CheckDate();//ostream& operator<<(ostream& out);//istream& operator>>(istream& in);void Print()const{cout << _year << "/" << _month << "/" << _day << endl;}int GetMonthDay(int year, int month){static int arr[] = { -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)){return 29;}return arr[month];}~Date(){_year = _month = _day = 0;}
private:int _year;int _month;int _day;
};bool Date::operator<(const Date& d) const
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}bool Date::operator<=(const Date& d)const
{return (*this < d) || *this == d;
}bool Date::operator>(const Date& d)const
{return !(*this <= d);
}bool Date::operator>=(const Date& d)const
{return !(*this < d);
}
bool Date::operator==(const Date& d)const
{return (_year == d._year &&_month == d._month &&_day == d._day);
}
bool Date::operator!=(const Date& d)const
{return !(*this == d);
}//Date& Date::operator+=(int day)
//{
//	return *this = *this + day;
//}
//
//Date Date::operator+(int day)
//{
//	Date tmp = *this;
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month));
//	{
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		tmp._month++;
//		if (tmp._month == 13)
//		{
//			tmp._year++;
//			tmp._month = 1;
//		}
//	}
//	return tmp;
//}Date& Date::operator+=(int day)
{if (day < 0){return *this -= -day;}_day += day;while (_day > GetMonthDay(_year, _month)){//因为加到大于本月的天数了//所以要减去本月已经过完的天数_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_month = 1;_year++;}}return *this;
}Date Date::operator+(int day)const
{Date tmp = *this;tmp += day;return tmp;
}//Date& Date::operator-=(int day)
//{
//	return *this = *this - day;
//}//Date Date::operator-(int day)
//{
//	Date tmp = *this;
//	if (day < 0)
//	{
//		return tmp + (-day);
//	}
//	tmp._day -= day;
//	while (tmp._day < GetMonthDay(tmp._year, tmp._day))
//	{
//		tmp._month--;
//		if (tmp._month == 0)
//		{
//			tmp._month = 12;
//			tmp._year--;
//		}
//		tmp._day += GetMonthDay(tmp._year, tmp._day);
//	}
//	return tmp;
//}Date& Date::operator-=(int day)
{if (day < 0){return *this += -day;}_day -= day;while (_day < 0){_month--;if (_month == 0){_month = 12;}//当前月份已经减完了,所以要借上个月的天数_day += GetMonthDay(_year, _month);}return *this;
}Date Date::operator-(int day)const
{Date tmp = *this;tmp -= day;return tmp;
}Date& Date::operator++()
{*this += 1;return *this;
}Date Date::operator++(int)
{Date tmp = *this;*this += 1;return tmp;
}//const Date* const this
int Date::operator-(const Date& d) const
{Date Max = *this;Date Min = d;int sum = 0;int flag = 1;//	<小于返回真,大于返回0//	<就是说如果是Min大于Max的话那就要交换了if (Max < Min){Max = d;Min = *this;flag = -1;}while (Min != Max){Min += 1;sum++;}return flag * sum;
}//ostream& Date::operator<<(ostream& out)
//{
//	cout << _year << "/" << _month << "/" << _day << endl;
//	return out;
//}
//
//istream& Date::operator>>(istream& in)
//{
//	cout << "请输入年月日" << endl;
//	in >> _year >> _month >> _day;
//	return in;
//}//在类外面是使用不了类里面私有的成员变量,所以我们先让其变成共有的
//等等解决方法就是有元
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "/" << d._month << "/" << d._day << endl;return out;
}istream& operator>>(istream& in, Date& d)
{cout << "请输入年月日" << endl;in >> d._year >> d._month >> d._day;if (!d.CheckDate());return in;
}bool Date::CheckDate()
{if (_month > 12 || _month < 1 || _day < 1 || _day > GetMonthDay(_year, _month)){cout << "日期非法" << endl;return false;}return true;
}

将上面重点模块进行抽丝剥茧

首先我们要先忽略掉上面有些函数后面加的const,等到下一个小章节1.4,我们再来说明




上面我们是先写了加等,然后用加来调用加等。那如果我们先写加,后面用加等来调用加,这是否是可行的呢?

首先这肯定是可行的,但同样都是实现一样的效果,先实现加后用加等来调用加的效率就会比先实现加等后用加来调用加等的效率还要低,因为多产生了2次的拷贝构造。




operator<<(ostream& out)和operator>>(istream& in)放到类里面,变成成员函数会发生什么呢?

然后将其变成全局函数也有个问题:函数对象内无法访问私有成员变量


1.4 const修饰成员变量 

  • 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
  • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。 const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this(const 修饰*this,本质改变this类型)
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 2024, int month = 11, int day = 30){_year = year;_month = month;_day = day;}//本来是Date* const this//加const就变成const Date* const thisvoid Print() const{cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{//这⾥⾮const对象也可以调⽤const成员函数是⼀种权限的缩⼩ Date d1(2024, 6, 1);d1.Print();const Date d2(2024, 11, 30);d2.Print();return 0;
}

2. 构造函数之初始化列表

  • 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有⼀种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
  • 每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
  • 引用成员变量const成员变量没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
  • C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
  • 尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
  • 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致。

初始化列表总结:

  • 无论是否显示写初始化列表,每个构造函数都有初始化列表;
  • 无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化;

#include <iostream>
using namespace std;class Time
{
public:Time(int hour):_hour(hour){cout << "Time(int hour)" << endl;}
private:int _hour;
};class Date
{
public:Date(int& ret, int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day),_i(3),ref(ret),t1(3){}private:int _year;int _month;int _day;const int _i = 2;//const修饰的成员变量(给了缺省值)int& ref;//引用Time t1;//没有默认构造的自定义类型
};int main()
{int x = 2;Date d1(x, 2024, 11, 28);return 0;
}

对上面代码的抽丝剥茧

补充一下:如果是引用传参一般都是用半缺省,引用放在最左边

 总结一下:实践中尽可能使用初始化列表初始化,不方便在使用函数体初始化

#include <iostream>
using namespace std;class Time
{
public:Time(int hour):_hour(hour){cout << "Time(int hour)" << endl;}private:int _hour;
};class Date
{
public:Date(int& ref):_month(2),ret(ref),ptr((int*)malloc(40)),_t1(2){//将这40个字节空间全部初始化为0memset(ptr, 0, 40);cout << "Date()" << endl;}void Print()const{cout << _year << "/" << _month << "/" << _day;}
private:int _year = 1;int _month = 1;int _day = 1;const int _i = 1;int& ret;int* ptr;Time _t1;
};int main()
{int x = 2;Date d1(x);return 0;
}

#include <iostream>
using namespace std;class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}private:int _a2;int _a1;
};int main()
{A aa(1);aa.Print();
}

运行结果是:D

因为:成员变量在类中声明次序就是其在初始化列表中初始化顺序,与其在初始化列表中的先后次序无关

 3. 类型转换

  • C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
  • 构造函数前⾯加explicit就不再支持隐式类型转换。
  • 类类型的对象之间也可以隐式转换,需要相应的构造函数支持。
#include <iostream>
using namespace std;class A
{
public:A(int a):_a1(a){cout << "class A" << endl;}A(int a1, int a2):_a1(a1),_a2(a2){cout << "A(int a1, int a2)" << endl;}A(const A& a){_a1 = a._a1;_a2 = a._a2;cout << "A(const A& a)" << endl;}int Get()const{return _a1 + _a2;}void Print() const{cout << _a1 << " " << _a2 << endl;}
private:int _a1 = 1;int _a2 = 2;
};class B
{
public://explicit B(const A& a)B(const A& a):_b(a.Get()){cout << "class B" << endl;}
private:int _b = 0;
};int main()
{//构造A a(2024);//拷贝构造A b = a;//拷贝构造A c(a);//构造加拷贝构造//因为A类型的单参数构造函数传的是int类型//所以这里把3构造一个A类型的临时对象//然后把临时对象通过拷贝构造到d对象中//编译器遇到构造加拷贝构造,会把其优化成构造A d = 3;//引用的是4拷贝构造出来的临时对象const A& ret = 4;//逗号表达式,相对与2A aa3 = (1, 2);A aa4 = { 1,2 };B b1 = aa3;A aa5 = { 5, 6 };B b2 = aa5;const B& b3 = aa5;return 0;
}

对上面代码的抽丝剥茧



类类型的对象之间也可以隐式转换,需要相应的构造函数支持。

3. static成员 

  • 用static修饰的成员变量,称之为静态成员变量,静态成员变量一定要在类外进行初始化。
  • 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
  • 用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
  • 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
  • 非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
  • 突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量和静态成员函数。
  • 静态成员也是类的成员,受public、protected、private访问限定符的限制。
  • 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是给构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。
#include <iostream>
using namespace std;class A
{
public:A(){_scount++;}A(const A& ret){_scount++;}~A(){_scount--;}static int GetACount(){return _scount;}
private:static int _scount;
};
//类外定义
int A:: _scount = 10;int main()
{cout << A::GetACount() << endl;A a1;A a2(a1);a1.GetACount();return 0;
}

上面代码的补充说明:



求1+2+3+...+n_牛客题霸_牛客网

#include <iostream>
using namespace std;class Sum
{
public:Sum(){_ret += _i;++_i;}static int GetRet(){return _ret;}
private:static int _i;static int _ret;
};int Sum::_i = 1;
int Sum::_ret = 0;class Solution {
public:int Sum_Solution(int n) {// 变⻓数组 //定义n个Sum类型的数组就要构造n个Sum类型的对象//因此可以在构造的时候++Sum arr[n];return Sum::GetRet();}
};

4. 友元

  • 友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到⼀个类的里面。
  • 外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
  • 一个函数可以是多个类的友元函数。
  • 友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
  • 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
  • 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
  • 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

友元函数

#include <iostream>
using namespace std;//前置声明,否则A的友元函数声明编译器不认识B
class B;
class A
{friend void Func(const A& a, const B& b);
private:int _a1 = 1;int _a2 = 2;
};class B
{friend void Func(const A& a, const B& b);
private:int _b1 = 1;int _b2 = 2;
};void Func(const A& a, const B& b)
{cout << a._a1 << " " << b._b1 << endl;
}int main()
{A aa1;B aa2;Func(aa1, aa2);
}

友元类

#include <iostream>
using namespace std;class A
{//友元声明friend class B;
private:int _a1 = 1;int _a2 = 2;
};class B
{
public:void Func1(const A& aa1){cout << aa1._a1 << endl;cout << _b1 << endl;}void Func2(const A& aa2){cout << aa2._a2 << endl;cout << _b2 << endl;}
private:int _b1 = 3;int _b2 = 4;
};int main()
{A a1;B b2;b2.Func1(a1);b2.Func2(a1);return 0;
}

5. 内部类

  • 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
  • 内部类默认是外部类的友元类。
  • 内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。
#include <iostream>
using namespace std;class A
{
private:static int _k;int _h = 1;
public:class B{public:void foo(const A& a){//这里可以直接使用_k的原因是B是A的有元可以直接访问私有//并且_k并不存在对象中,所以不用对象来调用也是可以的cout << _k << endl;//_h不可以直接写的原因是_h是A的成员变量//虽然B中是可以访问A中私有的,但是this指针只能访问B中的成员变量//_h不是B中的成员变量,所以只能通过A类型对象.来访问了cout << a._h << endl;}};
};int main()
{cout << sizeof(A) << endl;//B b这样定义是错误的,因为找不到B类A::B b;A aa;b.foo(aa);return 0;
}

6. 日期差

日期差值

#define _CRT_SECURE_NO_WARNINGS 1
#include <climits>
#include <iostream>
using namespace std;class Date
{
public:int operator-(Date& d2);bool operator<(const Date& d2);bool operator==(const Date& d)const;void operator=(const Date& d);bool operator!=(const Date& d)const;Date& operator++();Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};bool Date::operator<(const Date& d2)
{if (this->_year < d2._year){return true;}else if (this->_year == d2._year){if (this->_month < d2._month){return true;}else if (this->_month == d2._month){if (this->_day < d2._day){return true;}}}return false;
}bool Date::operator==(const Date& d)const
{return (_year == d._year &&_month == d._month &&_day == d._day);
}bool Date::operator!=(const Date& d)const
{return !(*this == d);
}int RerDay(int year, int month)
{static int arr[] = { -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))return 29;return arr[month];
}Date& Date::operator++()
{this->_day++;if (this->_day > RerDay(this->_year, this->_month)){this->_month++;this->_day = 1;if (this->_month == 13){this->_year++;this->_month = 1;}}return *this;
}void Date::operator=(const Date& d)
{this->_year = d._year;this->_month = d._month;this->_day = d._day;
}//*this 2024 11 1
// d    2024 11 12
int Date::operator-(Date& d2)
{//2024 11  1Date Max = *this;//2024 11 12//小的减大的得到的是负数//2024 11 12 - 2024 11 1得到的才是正数Date Min = d2;int flag = 1;int sum = 0;if (Max < Min){Max = d2;Min = *this;flag = -1;}while (Max != Min){++Min;sum++;}return flag * sum;
}int main() {int year1, month1, day1;int year2, month2, day2;scanf("%4d%2d%2d", &year1, &month1, &day1);scanf("%4d%2d%2d", &year2, &month2, &day2);//2024 11 30Date d1(year1, month1, day1);//2023 2  9Date d2(year2, month2, day2);//ret = 0假int rer = d1 < d2;int flag = 1;if (rer == 0){flag = -1;}int sum = 0;int MaxY = year1;int MinY = year2;if (MinY > MaxY){MinY = year1;MaxY = year2;}// k = 1000int k = MaxY - MinY;int ret = 0;if (rer){Date d3(year1, month2, day2);ret = d3 - d1;}else{//d1 = 2024 11 30//d2 = 2023 2  9//d3 = 2023 11 30//构造一个日期//让把最大那个年份改成最小的那个年份Date d3(year2, month1, day1);//计算2023 11 30号到2023 2 9号经过了多少天ret = d3 - d2;}//MinY = 2023 MaxY = 2024 ,他们之间差一年 for (int i = MinY; i < MaxY; i++){if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0){sum += 366;}sum += 365;}//差了几年算出来了,就要加上ret,ret:除去年还差几天if (ret < 0){sum += ret;}else{sum += ret + 1;}sum *= flag;cout << sum << endl;return 0;
}

当 ret < 0 时,说明在之前计算两个日期差的过程中可能由于某种原因,得到的天数差是负数。但实际上,在整个计算两个日期相隔天数的逻辑中,这里的负数天数差也是有效的,它表示了一种相对的时间间隔关系。所以在这种情况下,直接将这个负数的天数差累加到 sum 中,不需要进行额外的调整.

ret >= 0
在整个计算两个日期相隔天数的逻辑中,除了这个天数差之外,还需要考虑年份之间的间隔天数。例如,如果两个日期分别是 2024-01-01 和 2025-01-01,通过 Date 类的减法运算符重载函数得到的 ret 可能是 365(假设不考虑闰年等特殊情况),但实际上两个日期之间相隔的天数应该是 366(因为要包含两个日期当天,即从 2024-01-01 到 2025-00-31 是 365 天,再加上 2025-01-01 这一天)。所以当 ret >= 0 时,需要在累加 ret 到 sum 的时候再加 1,以准确计算出两个日期之间相隔的总天

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482752.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle RAC各版本集群总结和常用命令汇总

oracle RAC学习 RAC介绍 RAC&#xff1a;高可用集群&#xff0c;负载均衡集群&#xff0c;高性能计算集群 RAC是⼀种⾼可⽤&#xff0c;⾼性能&#xff0c;负载均衡的share-everything的集群 8i:内存融合雏形 内存融合雏形&#xff08;Oracle Parallel Server&#xff09;…

今天我们来聊聊Maven中两个高级的概念—— 插件和目标

插件&#xff08;plugin&#xff09; Maven的核心是一个插件执行框架;所有的工作都是由插件完成的。 Maven中Plugin分为两种类型&#xff1a; build类型Plugin只能在build阶段执行&#xff0c;在POM中需要在 <build/> 标签下进行配置。 reporting类型&#xff1a;在si…

[每周一更]-(第125期):模拟面试|NoSQL面试思路解析

文章目录 39|Elasticsearch 高可用:怎么保证 Elasticsearch 的高可用?1. Elasticsearch 的节点有什么角色?一个节点可以扮演多个角色吗?2. 在实践中,怎么合理安排不同节点扮演的角色?3. 什么是候选主节点和投票节点?投票节点可以被选为主节点吗?为什么要引入投票节点?…

【人工智能】Transformers之Pipeline(二十七):蒙版生成(mask-generation)

​​​​​​​ 目录 一、引言 二、蒙版生成&#xff08;mask-generation&#xff09; 2.1 概述 2.2 facebook/sam-vit-base 2.3 pipeline参数 2.3.1 pipeline对象实例化参数 2.3.2 pipeline对象使用参数 2.3.3 pipeline对象返回参数 2.4 pipeline实战 2.5 模型排…

蓝牙设备驱动开发

文章目录 一、蓝牙协议架构二、蓝牙协议的HCI传输层三、编程框架 一、蓝牙协议架构 蓝牙是无线数据和语音传输的开放式标准&#xff0c;它将各种通信设备、计算机及其终端设备、各种数字数据系统、甚至家用电器采用无线方式联接起来。它的传输距离为10cm&#xff5e;10m&#…

【Linux】开启你的Linux之旅:初学者指令指南

Linux相关知识点可以通过点击以下链接进行学习一起加油&#xff01; 在 Linux 开发中&#xff0c;GDB 调试器和 Git 版本控制工具是开发者必备的利器。GDB 帮助快速定位代码问题&#xff0c;Git 则提供高效的版本管理与协作支持。本指南将简明介绍两者的核心功能与使用技巧&…

你还没有将 Siri 接入GPT对话功能吗?

由于各种原因&#xff0c;国内ios用户目前无缘自带 AI 功能&#xff0c;但是这并不代表国内 ios 无法接入 AI 功能&#xff0c;接下来手把手带你为iPhone siri 接入 gpt 对话功能。 siri 接入 chatGPT 暂时还无法下载 ChatGPT app&#xff0c;或者没有账号的读者可以直接跳到…

bash命令缓存导致命令执行失败的问题

1、问题背景 为了修复老版本 vsftpd 的安全漏洞&#xff0c;需要把生产环境上 vsftpd 版本升级到 vsftpd-3.0.5&#xff0c;因为直接使用 rpm 包的方式进行升级还涉及到下层依赖包的升级(生产环境上的依赖包版本不能随意变更&#xff0c;可能会影响其他上层应用)&#xff0c;所…

import.meta.glob动态加载图片

import.meta.glob 基于Vite&#xff08;Vue 3 默认构建工具&#xff09;&#xff0c;用于动态导入模块&#xff0c;特别是当你需要批量导入文件或模块时. const modules import.meta.glob(/path/to/files/**/*.js);注意&#xff1a;import.meta.glob 是针对 源代码&#xff…

困扰解决:mfc140u.dll丢失的解决方法,多种有效解决方法全解析

当电脑提示“mfc140u.dll丢失”时&#xff0c;这可能会导致某些程序无法正常运行&#xff0c;给用户带来不便。不过&#xff0c;有多种方法可以尝试解决这个问题。这篇文章将以“mfc140u.dll丢失的解决方法”为主题&#xff0c;教大家有效解决mfc140u.dll丢失。 判断是否是“mf…

状态模式S

状态模式&#xff08;State Pattern&#xff09;是行为设计模式的一种&#xff0c;它允许一个对象在其内部状态发生改变时改变其行为。这个对象被视为类型的有限状态机&#xff08;Finite State Machine&#xff09;。 在状态模式中&#xff0c;我们创建表示各种状态的对象和一…

Webman中实现定时任务

文章目录 Webman中实现定时任务一、引言二、安装与配置1、安装Crontab组件2、创建进程文件3、配置进程文件随Webman启动4、重启Webman5、Cron表达式&#xff08;补充&#xff09;例子 三、使用示例四、总结 Webman中实现定时任务 一、引言 在现代的后端开发中&#xff0c;定时…

瀚高创库建表pgsql

1.瀚高下载地址&#xff1a; 下载 (highgo.com)https://www.highgo.com/down_main.html 2.瀚高linux安装 上传deb文件到ubuntu系统中 执行 dpkg -i hgdb-see-4.5.8-fe4791c.x86_64.deb 命令安装数据库 安装完成后&#xff0c;会在/opt 目录下生成安装目录 数据库安装完毕后…

5.11【机器学习】

先是对图像进行划分 划分完后&#xff0c; 顺序读取文件夹&#xff0c;在文件夹里顺序读取图片&#xff0c; 卷积层又称为滤波器&#xff0c;通道是说滤波器的个数&#xff0c;黑白通道数为1&#xff0c;RGB通道个数为3 在输入层&#xff0c;对于输入层而言&#xff0c;滤波…

Ajax基础总结(思维导图+二维表)

一些话 刚开始学习Ajax的时候&#xff0c;感觉很模糊&#xff0c;但是好像学什么都是这样的&#xff0c;很正常&#xff0c;但是当你学习的时候要持续性敲代码&#xff0c;边敲代码其实就可以理解很多了。然后在最后的总结&#xff0c;其实做二维表之后&#xff0c;就可以区分…

Python 自动化办公的 10 大脚本

大家好&#xff0c;我是你们的 Python 讲师&#xff01;今天我们将讨论 10 个实用的 Python 自动化办公脚本。这些脚本可以帮助你简化日常工作&#xff0c;提高效率。无论是处理 Excel 文件、发送邮件&#xff0c;还是自动化网页操作&#xff0c;Python 都能派上用场。 1. 批量…

【北京迅为】iTOP-4412全能版使用手册-第二十章 搭建和测试NFS服务器

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

[Linux] 信号(singal)详解(一)

标题&#xff1a;[Linux] 信号(singal)详解 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、认识信号 1、认识信号 2、信号特点 3、基本概念 二、信号的产生&#xff08;5种方式&#xff09; 三、信号的保存 正文开始&#xff1a; 一、认识信号 1、认识信…

简单介绍下 VitePress 中的 vp-doc 和 vp-raw

VitePress 是一个轻量级的静态网站生成器&#xff0c;专为快速构建文档网站而设计。它是基于 Vite 和 Vue 3 构建的&#xff0c;旨在提供快速的开发体验和高效的构建过程。 存在两个需要注意的点&#xff1a;vp-doc 和 vp-raw&#xff0c;它们代表了不同的 CSS 样式类和用途&a…

服务器数据恢复—服务器raid0阵列硬盘指示灯显示黄颜色的数据恢复案例

服务器数据恢复环境&故障情况&#xff1a; 某品牌服务器上有一组由两块SAS硬盘组建的raid0阵列&#xff0c;上层是windows server操作系统ntfs文件系统。服务器上一个硬盘指示灯显示黄颜色&#xff0c;该指示灯对应的硬盘离线&#xff0c;raid不可用。 服务器数据恢复过程…