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来完成,在获取返回的累加值时,可以单独在类内写个函数返回私有成员变量,该函数可以是静态成员函数,这样就可以指定类域去调用,不需要借助对象了。也可以借助友元。

  • 代码:
#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、日期差值

  • 题目:

  • 链接直达:

日期差值

  • 思路:采用相对差距
  1. 分别求出每一个日期与0000年0月1日距离的天数
  2. 两个距离天数相减即可得到两个日期相差的天数
  • 代码:
#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次拷贝构造,分别为如下四次:

  1. 传值传参x,拿x初始化u,同自定义类型,为第一次拷贝构造,如若引用传参,则不是拷贝构造
  2. 拿u初始化v,第二次拷贝构造
  3. Weight w = v; 即使是 = ,但此处为第三次拷贝构造
  4. 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次拷贝构造。

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 29
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三分苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值