• C++类和对象经典oj


    目录

    1、求1+2+3+...+n

    2、计算日期到天数转换

    3、日期差值

    4、打印日期

    5、日期累加

    6、拷贝构造调用次数问题


    1、求1+2+3+...+n

    • 题目:

    • 链接直达:

    求1+2+3+……+n

    • 思路:

    这里我可以自己单独定义一个Sum类,专门进行求和,我定义n个对象,它就会调用n次构造函数,此时就可以在构造函数内实现累加,为了实现累加,需要在Sum类里设定两个静态成员变量,因为静态成员属于整个类,以此确保每次访问的变量都是同一个,最后,返回累加的值即可。

    • 注意:

    如若不支持变长数组,我们只能用new来完成,在获取返回的累加值时,可以单独在类内写个函数返回私有成员变量,该函数可以是静态成员函数,这样就可以指定类域去调用,不需要借助对象了。也可以借助友元。

    • 代码:
    1. #include<iostream>
    2. using namespace std;
    3. class Sum
    4. {
    5. public:
    6. Sum() //构造函数内实现累加
    7. {
    8. _ret += _i;
    9. _i++;
    10. }
    11. int GetRet() //static int GetRet() 也可以是静态成员函数
    12. {
    13. return _ret; //返回获取的求和值
    14. }
    15. private:
    16. //静态成员变量类内声明
    17. static int _i;
    18. static int _ret;
    19. };
    20. //静态成员变量类外定义
    21. int Sum::_i = 1;
    22. int Sum::_ret = 0;
    23. class Solution {
    24. public:
    25. int Sum_Solution(int n) {
    26. Sum a[n]; //支持变长数组可以这样写
    27. return a[0].GetRet(); //注意通过对象去调用成员函数
    28. //return Sum::GetRet();静态成员函数支持用类域访问
    29. /* 如若不支持变长数组,就用new来开辟n个空间
    30. Sum* ptr = new Sum[n];
    31. return ptr->GetRet();
    32. */
    33. }
    34. };

    此题我们还可以使用先前学过的内部类来解决,把Sum类放到Solution类里,这样Sum类就是Solution类的友元。

    1. class Solution
    2. {
    3. private:
    4. class Sum //Sum类就是Solution类的友元
    5. {
    6. public:
    7. Sum()
    8. {
    9. _ret += _i;
    10. _i++;
    11. }
    12. };
    13. public:
    14. int Sum_Solution(int n) {
    15. //Sum a[n];
    16. Sum* ptr = new Sum[n];
    17. return _ret; //可以直接返回_ret
    18. }
    19. private:
    20. static int _i;
    21. static int _ret;
    22. };
    23. int Solution::_i = 1;
    24. int Solution::_ret = 0;

    2、计算日期到天数转换

    • 题目:

    • 链接直达:

    计算日期到天数转换

    • 思路:

    此题有一个非常巧的解法:除了考虑到平年和闰年,1月到任何一个月的天数都是固定的,因此,我们开辟一串数组(长13),初始化daysArray[month]即为从1月到month月的所有天数,并且此数组我们假定为平年,这样的话,我们输出完日期后,只需要判断平闰年再加上返回daysArray[month -1]+day即可。此法也就不需要我们手写日期类等其他复杂的步骤了。

    • 代码:
    1. #include<iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. int year, month, day;
    6. int daysArray[13] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };//1到month月的天数固定
    7. while(cin >> year >> month >> day)
    8. {
    9. int sum = daysArray[month - 1] + day; //假定平年的天数
    10. if ((month > 2) && (year % 4 == 0 && year % 100 != 0 || year %400 == 0))
    11. {
    12. sum += 1;//闰年,sum+1
    13. }
    14. cout << sum << endl;
    15. }
    16. return 0;
    17. }

    3、日期差值

    • 题目:

    • 链接直达:

    日期差值

    • 思路:采用相对差距
    1. 分别求出每一个日期与0000年0月1日距离的天数
    2. 两个距离天数相减即可得到两个日期相差的天数
    • 代码:
    1. #include<iostream>
    2. using namespace std;
    3. static int DayMonth[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    4. //判断是否是闰年
    5. bool IsLeapYear(int year)
    6. {
    7. return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    8. }
    9. int CountDay(int year, int month, int day)
    10. {
    11. int ret = 0;
    12. //先算0-year年的天数
    13. ret += year * 365 + year / 4 - year / 100 + year / 400;//year/4-year/100+year/400计算的是其中闰年的天数
    14. //再算0-month月的天数
    15. for (int i = 1; i < month; i++)
    16. {
    17. ret += DayMonth[i];
    18. if (i == 2 && IsLeapYear(year))
    19. ret += 1; //闰年闰月29天,记得+1
    20. }
    21. //最后返回,记得加上day
    22. return ret + day;
    23. }
    24. int main()
    25. {
    26. int year1, month1, day1, year2, month2, day2;
    27. while (~scanf("%4d%2d%2d", &year1, &month1, &day1))
    28. {
    29. scanf("%4d%2d%2d", &year2, &month2, &day2);
    30. int count1 = CountDay(year1, month1, day1);
    31. int count2 = CountDay(year2, month2, day2);
    32. //日期差值题意要求绝对值,且最后还需要+1,以此满足题意
    33. cout << abs(count1 - count2) + 1 << endl;
    34. }
    35. }
    • 优化:

    针对上述获取天数的函数,可以不需要写for循环从而达到计算0-month月的天数,如下:

    1. //平年从1月到n月的天数
    2. int mon[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
    3. //给出年月日,计算距离0000年0月1日的天数和
    4. int CountDay(int y, int m, int d)
    5. {
    6. // 计算0-y年的天数
    7. int yearDay = y * 365 + y / 4 - y / 100 + y / 400;
    8. // 计算到0-m月的天数
    9. int monthDay = mon[m - 1];
    10. if (m > 2 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0))
    11. monthDay += 1;
    12. return yearDay + monthDay + d;
    13. }
    14. int main()
    15. {
    16. //……
    17. }

    4、打印日期

    • 题目:

    • 链接直达:

     打印日期

    • 思路:

    此题我们设计一个循环,判断所输入的总天数是否大于month月的天数,若大于,就--month月的总天数,直至小于为止,在此期间不断更新month和总天数。还要记得判断闰年的二月是29天。

    • 代码:
    1. #include<iostream>
    2. using namespace std;
    3. static int mArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    4. int main()
    5. {
    6. int year, day;
    7. while (cin >> year >> day)
    8. {
    9. if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
    10. {
    11. mArray[2] = 29; //闰年+1
    12. }
    13. int month = 1;
    14. while (day>mArray[month]) //确保日期合法
    15. {
    16. day -= mArray[month]; //更新day,使其小于一个月
    17. month++; //更新月数
    18. }
    19. printf("%d-%02d-%02d", year, month, day);
    20. }
    21. return 0;
    22. }

    5、日期累加

    • 题目:

    • 链接直达:

    日期累加

    • 思路:

    首先把要加的天数先加到day上,如若我day的天数>我当月的总天数,则day-=当月数,并更新month++,当我month+到>12时,就更新year++,month=1,整个过程切记考虑到闰年的情况

    • 代码:
    1. #include<iostream>
    2. using namespace std;
    3. static int mArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    4. int main()
    5. {
    6. int m = 0;
    7. int year, month, day, n;
    8. cin >> m;
    9. while (m--)
    10. {
    11. cin >> year >> month >> day >> n;
    12. day += n;
    13. while (day > mArray[month]) //当day大于当月天数时继续循环
    14. {
    15. if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
    16. mArray[2] = 29;
    17. else
    18. mArray[2] = 28;
    19. day -= mArray[month];//更新day
    20. month++;
    21. if (month > 12)//确保日期合法
    22. {
    23. year++;
    24. month = 1;
    25. }
    26. }
    27. printf("%d-%02d-%02d\n", year, month, day);
    28. }
    29. return 0;
    30. }

    6、拷贝构造调用次数问题

    请问如下的代码调用了几次拷贝构造?

    1. class Weight
    2. {
    3. public:
    4. Weight()//构造
    5. {
    6. cout << "Weight()" << endl;
    7. }
    8. Weight(const Weight& w) //拷贝构造
    9. {
    10. cout << "Weight(const Weight& w)" << endl;
    11. }
    12. };
    13. Weight f(Weight u) //传值传参
    14. {
    15. Weight v(u);//拿u初始化v,拷贝构造
    16. Weight w = v; //虽然是赋值符号,依然拷贝构造
    17. return w; //传值返回会再次拷贝构造
    18. }
    19. int main()
    20. {
    21. Weight x;
    22. f(x); //拿x初始化u,传值传参为拷贝构造
    23. }

    从运行结果上看,是调用了4次拷贝构造,分别为如下四次:

    1. 传值传参x,拿x初始化u,同自定义类型,为第一次拷贝构造,如若引用传参,则不是拷贝构造
    2. 拿u初始化v,第二次拷贝构造
    3. Weight w = v; 即使是 = ,但此处为第三次拷贝构造
    4. return w; 传值返回,第四次拷贝构造 

    这里补充一个知识点:匿名对象

    Weight();//匿名对象,声明周期只在这一行
    

    匿名对象的声明周期只在这一行,我们可以使用匿名对象去调用成员函数,就不需要单独定义对象,然后再调用成员函数:

    当我利用匿名对象这样写时:

    正常情况下,我应该还是先构造,传值传参调用拷贝构造,然后拿u初始化v拷贝构造……所以应该是4次拷贝构造,但是这里却是3次,原因就是编译器对于在一个表达式里头,若出现连续的构造+拷贝构造,那么就会优化合二为一成构造。次优化类似于我们先前讲解过的:

    1. int main()
    2. {
    3. Weight a1(1);
    4. Weight a2 = 2; //先构造 Weight(2) -> 再拷贝构造 Weight a2(Weight(2)) --> 优化 -- > 直接构造
    5. }

    再看一个例子:

    这里只调用了4次拷贝构造,按理说先传值传参调用1次,进入函数里又调用2次,最后传值返回调用1次拷贝构造给临时对象,最后临时对象再拷贝构造给ret,理应5次拷贝构造。 综上,我们得出结论:

    • 连续的构造 + 拷贝构造 优化成 构造
    • 连续的拷贝构造 + 拷贝构造 优化成 一次拷贝构造

    这么看的话,上述的4和5合二为一。再来看一个题:

    1. //以下代码共调用多少次拷贝构造函数?
    2. Widget f(Widget u)
    3. {
    4. Widget v(u);
    5. Widget w=v;
    6. return w;
    7. }
    8. main(){
    9. Widget x;
    10. Widget y=f(f(x));
    11. }

    综上一共7次拷贝构造。

  • 相关阅读:
    2022 面试必刷 461 道大厂架构面试真题汇总 + 面经 + 简历模板
    :style在列表中动态配置背景图片
    k8s存储学习 emptyDir 卷
    HDFS、MapReduce原理--学习笔记
    正点原子lwIP学习笔记——Socket接口TCP实验
    快速部署 Garnet
    Git存储原理——树对象
    C++模板从入门到入土
    盲盒小程序有哪些特色功能
    MyBatis学习:自定义别名
  • 原文地址:https://blog.csdn.net/bit_zyx/article/details/125025254