【C++OJ_单继承】新旧身份证(继承)

此博客介绍了如何使用C++编程实现日期类CDate和旧身份证类COldID,并在此基础上派生新身份证类CNewID,涉及到日期合法性检查、身份证号码的校验码生成及验证、有效期判断等功能。博客通过实例展示了类的继承和对象的使用,以及对身份证号码合法性的全面验证过程。

【C++OJ_单继承】新旧身份证(继承)

题目描述

按下述方式定义一个日期类CDate和描述15位身份证号的旧身份证类COldID:

class CDate

{

private:

int year, month, day;

public:

CDate(int,int,int);

bool check(); //检验日期是否合法

bool isLeap();

void print();

};

class COldID

{

private:

char *p_id15, *p_name; //15位身份证号码,姓名

CDate birthday; //出生日期

public:

COldID(char *p_idval, char *p_nameval, CDate &day);

bool check(); //验证15位身份证是否合法

void print();

~COldID();

};

然后以COldID为基类派生18位身份证号的新身份证类CNewID,并增加3个数据成员:p_id18(18位号码)、issueday(签发日期)和validyear(有效期,年数),并重新定义check()和print()。

身份证第18位校验码的生成方法:

1、将身份证号码前17位数分别乘以7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2。然后将其相加。

2、将17位数字和系数乘加的和除以11,得到余数。

3、余数与校验码的对应关系为1,0,X,9,8,7,6,5,4,3,2。也即:如果余数是3,身份证第18位就是9。如果余数是2,身份证的最后一位号码就是X。

主函数定义一个派生类对象,并用派生类对象调用check(),若返回false则输出“illegal id”否则调用print()输出身份证信息。check()对身份证合法性进行验证的规则:

  1. 确认15位身份证正确.

  2. 确认18位号码是从15位号码扩展的,且第18位校验码正确.

  3. 身份证中的出生日期合法.

  4. 身份证号码中不含非法字符.

  5. 身份证号码的长度正确.

  6. 身份证目前处于有效期内. (2017年5月10日)

输入

测试数据的组数 t

第一个人姓名、出生日期年月日、15位身份证号码、18位身份证号码、签发日期年月日、有效期(100年按长期处理)

第二个人姓名、出生日期年月日、15位身份证号码、18位身份证号码、签发日期年月日、有效期(100年按长期处理)

输出

第一个人姓名

第一个人18位身份证号信息(号码、签发日期和有效期)或"illegal id"

第二个人姓名

第二个人18位身份证号信息(号码、签发日期和有效期)或"illegal id"

输入样例

10
AAAA 1988 2 28 440301880228113 440301198802281133 2006 1 20 20
BBBB 1997 4 30 440301980808554 440301199808085541 2015 2 2 10
CCCC 1920 5 8 530102200508011 53010219200508011X 1980 3 4 30
DDDD 1980 1 1 340524800101001 340524198001010012 1998 12 11 20
EEEE 1988 11 12 110203881112034 110203198811120340 2007 2 29 20
FFFF 1964 11 15 432831641115081 432831196411150810 2015 8 7 100
GGGG 1996 12 10 44030196121010 44030119961210109 2014 6 7 20
HHHH 1988 7 21 440301880721X12 44030119880721X122 2006 5 11 20
IIII 1976 3 30 440301760330098 440301197603300983 2003 4 15 20
JJJJ 1955 9 5 440301550905205 440301195509051052 2004 6 4 100

输出样例

AAAA
440301198802281133 2006年1月20日 20年
BBBB
illegal id
CCCC
illegal id
DDDD
illegal id
EEEE
illegal id
FFFF
432831196411150810 2015年8月7日 长期
GGGG
illegal id
HHHH
illegal id
IIII
illegal id
JJJJ
illegal id

参考代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;

class CDate //日期类CDate
{
private:
    int year, month, day;

public:
    CDate(int y, int m, int d) : year(y), month(m), day(d) {} //构造函数,初始化参数列表
    int getYear() { return year; }                            //获取year
    int getMonth() { return month; }                          //获取month
    int getDay() { return day; }                              //获取day
    bool check()
    {                     //检验日期是否合法
        int monthDay[] = {// 2月如果是闰年为28天,否则是29天
                          31, isLeap() ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        if (day < 0 || year < 0 || month < 0 || year > 2015 || month > 12 || day > monthDay[month - 1]) //输入日期的合法性判断
            return 0;
        else
            return 1;
    }
    bool isLeap() //判断是否是闰年
    {
        return (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
    }
    void print() //打印函数
    {
        cout << year << "年" << month << "月" << day << "日";
    }
};

class COldID //描述15位身份证号的旧身份证类COldID
{
protected:
    char *p_id15, *p_name; //15位身份证号码,姓名
    CDate birthday;        //出生日期
public:
    COldID(char *p_idval, char *p_nameval, int y, int m, int d) : birthday(y, m, d) //构造函数,初始化参数列表
    {
        p_id15 = new char[strlen(p_idval) + 1]; //需要分配string内存问题,不然会出现bus error问题
        strcpy(p_id15, p_idval); //涉及指针,用strcpy
        p_name = new char[strlen(p_nameval) + 1];
        strcpy(p_name, p_nameval);
    }
    int check() //验证15位身份证是否合法
    {
        if (!birthday.check()) // 输入的日期是否合法.直接调用CDate的check()函数判断
            return 0;
        if ((*(p_id15 + 6) - '0') * 10 + (*(p_id15 + 7) - '0') != birthday.getYear() % 100 || // 3. 身份证中的出生日期合法,跟输入的出生日期进行对比
            (*(p_id15 + 8) - '0') * 10 + (*(p_id15 + 9) - '0') != birthday.getMonth() ||
            (*(p_id15 + 10) - '0') * 10 + (*(p_id15 + 11) - '0') != birthday.getDay())
            return 0;
        for (int i = 0; i < 15; i++) // 4.身份证号码中不含非法字符.
        {
            if ('0' > *(p_id15 + i) || *(p_id15 + i) > '9')
                return 0;
        }
        if (strlen(p_id15) != 15) // 5.身份证号码的长度正确.
            return 0;
        return 1; //如果合法,默认输出1
    }
    void print() //打印函数
    {
        cout << p_name << endl;
    }
    ~COldID() {} //析构函数
};
class CNewID : public COldID //以COldID为基类派生18位身份证号的新身份证类CNewID
{
    char *p_id18;   //18位号码
    CDate issueday; //签发日期
    int validyear;  //有效期,年数
public:
    CNewID(char *name, int yval, int mval, int dval, char *p15, char *p18, int yval2, int mval2, int dval2, int v) //构造函数,初始化
        : COldID(p15, name, yval, mval, dval), issueday(yval2, mval2, dval2), validyear(v)
    {
        p_id18 = new char[strlen(p18) + 1];
        strcpy(p_id18, p18);
    }
    char *NewNumID()
    {
        static char num[20]; //这里应该至少分配19。需要注意的是C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。所以这里需要使用static

        for (int i = 0; i < 6; i++) //前6位数保持不变,15变18的方法之前的实验做过
        {
            num[i] = *(p_id15 + i);
        }

        if (birthday.getYear() < 2000)
        {
            num[6] = '1';
            num[7] = '9';
        }
        else
        {
            num[6] = '2';
            num[7] = '0';
        }
        // int t = birthday.getYear(); //获取year,设置7、8位数
        // num[6] = t / 1000 ;
        // num[7] = t / 100 % 10 ;

        for (int i = 8; i < 17; i++) // 9-17位保持不变
        {
            num[i] = *(p_id15 + i - 2);
        }

        //辅助列表和变量,开始处理第18位
        int list1[] = {
            7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        char list2[12] = {
            '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2', '\0'};
        int sum = 0;
        for (int i = 0; i < 17; i++) //将身份证号码前17位数分别乘以7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2。然后将其相加。
        {
            sum += ((num[i] - '0') * list1[i]);
        }
        sum %= 11;            //将17位数字和系数乘加的和除以11,得到余数
        num[17] = list2[sum]; //余数与校验码的对应关系为1,0,X,9,8,7,6,5,4,3,2
        num[18] = '\0';       //最后记得封闭'\0'

        return num;
    }
    int valid() // 6. 判断身份证目前处于有效期内. (2017年5月10日)
    {
        return ((issueday.getYear() + validyear) > 2017 ||
                ((issueday.getYear() + validyear) == 2017 && issueday.getMonth() < 5) ||
                ((issueday.getYear() + validyear) == 2017 && issueday.getMonth() == 5 && issueday.getDay() < 10));
        // return ((issueday.getYear() + validyear) > 2015 ||
        //         ((issueday.getYear() + validyear) == 2015 && issueday.getMonth() < 4) ||
        //         ((issueday.getYear() + validyear) == 2015 && issueday.getMonth() == 4 && issueday.getDay() < 7));
    }
    bool check()
    {
        if (strcmp(NewNumID(), p_id18) != 0 || !issueday.check() || !valid()) // strcmp() 判断字符串相同时返回0
            return 0;
        // //6. 身份证目前处于有效期内. (2017年5月10日)
        // if ((issueday.getYear() + validyear) > 2017 ||
        //     ((issueday.getYear() + validyear) == 2017 && issueday.getMonth() < 5) ||
        //     ((issueday.getYear() + validyear) == 2017 && issueday.getMonth() == 5 && issueday.getDay() < 10))
        //     return 0;
        else
            return 1;
    }
    void print() //打印函数
    {
        cout << p_id18 << " ";
        issueday.print();
        if (validyear != 100)
            cout << " " << validyear << "年" << endl;
        else
            cout << " 长期" << endl;
    }
    ~CNewID() {} //析构函数
};

int main()
{
    int t, y1, m1, d1, y2, m2, d2, v;
    char name[10], p15[20], p18[20];
    cin >> t;
    while (t--)
    {
        cin >> name >> y1 >> m1 >> d1 >> p15 >> p18 >> y2 >> m2 >> d2 >> v;
        CNewID c(name, y1, m1, d1, p15, p18, y2, m2, d2, v);
        c.COldID::print();
        if (c.COldID::check() && c.check())
            c.print();
        else
            cout << "illegal id" << endl;
    }
    return 0;
}
### 西北农林科技大学 C++ OJ 继承 示例 题目 解法 在西北农林科技大学的C++在线判题系统(OJ)中,继承是一个常见的编程主题。以下是一些与继承相关的题目示例以及可能的解决方案。 #### 示例题目 1: 单继承 题目描述:定义一个基类 `Animal` 和派生类 `Dog`,其中 `Dog` 类继承自 `Animal` 类。要求实现以下功能: - 基类 `Animal` 包含一个虚函数 `makeSound()`。 - 派生类 `Dog` 重写该函数以输出 `"Woof!"`。 ```cpp #include <iostream> using namespace std; class Animal { public: virtual void makeSound() { cout << "Some generic sound" << endl; } }; class Dog : public Animal { public: void makeSound() override { cout << "Woof!" << endl; } }; int main() { Animal* myAnimal = new Dog(); myAnimal->makeSound(); // 输出 "Woof!" delete myAnimal; return 0; } ``` 上述代码展示了单继承的基本用法[^1]。 #### 示例题目 2: 多继承 题目描述:定义两个基类 `Swimmer` 和 `Runner`,以及一个派生类 `Dolphin`,要求 `Dolphin` 同时继承自 `Swimmer` 和 `Runner`。实现以下功能: - `Swimmer` 包含一个函数 `swim()`,输出 `"Swimming..."`。 - `Runner` 包含一个函数 `run()`,输出 `"Running..."`。 - `Dolphin` 能够调用这两个函数。 ```cpp #include <iostream> using namespace std; class Swimmer { public: void swim() { cout << "Swimming..." << endl; } }; class Runner { public: void run() { cout << "Running..." << endl; } }; class Dolphin : public Swimmer, public Runner {}; int main() { Dolphin d; d.swim(); // 输出 "Swimming..." d.run(); // 输出 "Running..." return 0; } ``` 这段代码展示了多继承的实现方式[^2]。 #### 示例题目 3: 虚继承 题目描述:定义三个类 `A`、`B` 和 `C`,其中 `B` 和 `C` 都继承自 `A`,而 `D` 同时继承自 `B` 和 `C`。为了避免菱形继承问题,使用虚继承。 ```cpp #include <iostream> using namespace std; class A { public: int x; A(int val) : x(val) {} }; class B : virtual public A { public: B(int val) : A(val) {} }; class C : virtual public A { public: C(int val) : A(val) {} }; class D : public B, public C { public: D(int val) : A(val), B(val), C(val) {} }; int main() { D obj(10); cout << obj.x; // 输出 10
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ferry_xie

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

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

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

打赏作者

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

抵扣说明:

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

余额充值