目录
1、求1+2+3+...+n
- 题目:
- 链接直达:
- 思路:
这里我可以自己单独定义一个Sum类,专门进行求和,我定义n个对象,它就会调用n次构造函数,此时就可以在构造函数内实现累加,为了实现累加,需要在Sum类里设定两个静态成员变量,因为静态成员属于整个类,以此确保每次访问的变量都是同一个,最后,返回累加的值即可。
- 注意:
如若不支持变长数组,我们只能用new来完成,在获取返回的累加值时,可以单独在类内写个函数返回私有成员变量,该函数可以是静态成员函数,这样就可以指定类域去调用,不需要借助对象了。也可以借助友元。
- 代码:
#include<iostream> using namespace std; class Sum { public: Sum() //构造函数内实现累加 { _ret += _i; _i++; } int GetRet() //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) { Sum a[n]; //支持变长数组可以这样写 return a[0].GetRet(); //注意通过对象去调用成员函数 //return Sum::GetRet();静态成员函数支持用类域访问 /* 如若不支持变长数组,就用new来开辟n个空间 Sum* ptr = new Sum[n]; return ptr->GetRet(); */ } };
此题我们还可以使用先前学过的内部类来解决,把Sum类放到Solution类里,这样Sum类就是Solution类的友元。
class Solution { private: class Sum //Sum类就是Solution类的友元 { public: Sum() { _ret += _i; _i++; } }; public: int Sum_Solution(int n) { //Sum a[n]; Sum* ptr = new Sum[n]; return _ret; //可以直接返回_ret } private: static int _i; static int _ret; }; int Solution::_i = 1; int Solution::_ret = 0;
2、计算日期到天数转换
- 题目:
- 链接直达:
- 思路:
此题有一个非常巧的解法:除了考虑到平年和闰年,1月到任何一个月的天数都是固定的,因此,我们开辟一串数组(长13),初始化daysArray[month]即为从1月到month月的所有天数,并且此数组我们假定为平年,这样的话,我们输出完日期后,只需要判断平闰年再加上返回daysArray[month -1]+day即可。此法也就不需要我们手写日期类等其他复杂的步骤了。
- 代码:
#include<iostream> using namespace std; int main() { int year, month, day; int daysArray[13] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };//1到month月的天数固定 while(cin >> year >> month >> day) { int sum = daysArray[month - 1] + day; //假定平年的天数 if ((month > 2) && (year % 4 == 0 && year % 100 != 0 || year %400 == 0)) { sum += 1;//闰年,sum+1 } cout << sum << endl; } return 0; }
3、日期差值
- 题目:
- 链接直达:
- 思路:采用相对差距
- 分别求出每一个日期与0000年0月1日距离的天数
- 两个距离天数相减即可得到两个日期相差的天数
- 代码:
#include<iostream> using namespace std; static int DayMonth[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; //判断是否是闰年 bool IsLeapYear(int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; } int CountDay(int year, int month, int day) { int ret = 0; //先算0-year年的天数 ret += year * 365 + year / 4 - year / 100 + year / 400;//year/4-year/100+year/400计算的是其中闰年的天数 //再算0-month月的天数 for (int i = 1; i < month; i++) { ret += DayMonth[i]; if (i == 2 && IsLeapYear(year)) ret += 1; //闰年闰月29天,记得+1 } //最后返回,记得加上day return ret + day; } int main() { int year1, month1, day1, year2, month2, day2; while (~scanf("%4d%2d%2d", &year1, &month1, &day1)) { scanf("%4d%2d%2d", &year2, &month2, &day2); int count1 = CountDay(year1, month1, day1); int count2 = CountDay(year2, month2, day2); //日期差值题意要求绝对值,且最后还需要+1,以此满足题意 cout << abs(count1 - count2) + 1 << endl; } }
- 优化:
针对上述获取天数的函数,可以不需要写for循环从而达到计算0-month月的天数,如下:
//平年从1月到n月的天数 int mon[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 }; //给出年月日,计算距离0000年0月1日的天数和 int CountDay(int y, int m, int d) { // 计算0-y年的天数 int yearDay = y * 365 + y / 4 - y / 100 + y / 400; // 计算到0-m月的天数 int monthDay = mon[m - 1]; if (m > 2 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)) monthDay += 1; return yearDay + monthDay + d; } int main() { //…… }
4、打印日期
- 题目:
- 链接直达:
- 思路:
此题我们设计一个循环,判断所输入的总天数是否大于month月的天数,若大于,就--month月的总天数,直至小于为止,在此期间不断更新month和总天数。还要记得判断闰年的二月是29天。
- 代码:
#include<iostream> using namespace std; static int mArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int main() { int year, day; while (cin >> year >> day) { if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { mArray[2] = 29; //闰年+1 } int month = 1; while (day>mArray[month]) //确保日期合法 { day -= mArray[month]; //更新day,使其小于一个月 month++; //更新月数 } printf("%d-%02d-%02d", year, month, day); } return 0; }
5、日期累加
- 题目:
- 链接直达:
- 思路:
首先把要加的天数先加到day上,如若我day的天数>我当月的总天数,则day-=当月数,并更新month++,当我month+到>12时,就更新year++,month=1,整个过程切记考虑到闰年的情况
- 代码:
#include<iostream> using namespace std; static int mArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int main() { int m = 0; int year, month, day, n; cin >> m; while (m--) { cin >> year >> month >> day >> n; day += n; while (day > mArray[month]) //当day大于当月天数时继续循环 { if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) mArray[2] = 29; else mArray[2] = 28; day -= mArray[month];//更新day month++; if (month > 12)//确保日期合法 { year++; month = 1; } } printf("%d-%02d-%02d\n", year, month, day); } return 0; }
6、拷贝构造调用次数问题
请问如下的代码调用了几次拷贝构造?
class Weight { public: Weight()//构造 { cout << "Weight()" << endl; } Weight(const Weight& w) //拷贝构造 { cout << "Weight(const Weight& w)" << endl; } }; Weight f(Weight u) //传值传参 { Weight v(u);//拿u初始化v,拷贝构造 Weight w = v; //虽然是赋值符号,依然拷贝构造 return w; //传值返回会再次拷贝构造 } int main() { Weight x; f(x); //拿x初始化u,传值传参为拷贝构造 }
从运行结果上看,是调用了4次拷贝构造,分别为如下四次:
- 传值传参x,拿x初始化u,同自定义类型,为第一次拷贝构造,如若引用传参,则不是拷贝构造
- 拿u初始化v,第二次拷贝构造
- Weight w = v; 即使是 = ,但此处为第三次拷贝构造
- return w; 传值返回,第四次拷贝构造
这里补充一个知识点:匿名对象
Weight();//匿名对象,声明周期只在这一行
匿名对象的声明周期只在这一行,我们可以使用匿名对象去调用成员函数,就不需要单独定义对象,然后再调用成员函数:
当我利用匿名对象这样写时:
正常情况下,我应该还是先构造,传值传参调用拷贝构造,然后拿u初始化v拷贝构造……所以应该是4次拷贝构造,但是这里却是3次,原因就是编译器对于在一个表达式里头,若出现连续的构造+拷贝构造,那么就会优化合二为一成构造。次优化类似于我们先前讲解过的:
int main() { Weight a1(1); Weight a2 = 2; //先构造 Weight(2) -> 再拷贝构造 Weight a2(Weight(2)) --> 优化 -- > 直接构造 }
再看一个例子:
这里只调用了4次拷贝构造,按理说先传值传参调用1次,进入函数里又调用2次,最后传值返回调用1次拷贝构造给临时对象,最后临时对象再拷贝构造给ret,理应5次拷贝构造。 综上,我们得出结论:
- 连续的构造 + 拷贝构造 优化成 构造
- 连续的拷贝构造 + 拷贝构造 优化成 一次拷贝构造
这么看的话,上述的4和5合二为一。再来看一个题:
//以下代码共调用多少次拷贝构造函数? Widget f(Widget u) { Widget v(u); Widget w=v; return w; } main(){ Widget x; Widget y=f(f(x)); }
综上一共7次拷贝构造。