C++ 类与对象及重载、内联、引用等知识点【练习题】(含OJ题、选择题等),也包括一些需要注意的知识点

目录

一.OJ题

1.日期差值_牛客题霸_牛客网

2.计算日期到天数转换_牛客题霸_牛客网

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

4.打印日期_牛客题霸_牛客网

5.日期累加_牛客题霸_牛客网

二.选择题

三.注意(部分较为简单的选择题的总结)


一.OJ题

1.日期差值_牛客题霸_牛客网

(1)描述:

有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天

输入描述:

有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD

输出描述:

每组数据输出一行,即日期差值

示例1

输入:

20110412
20110422

输出:

11

(2)思路

        因为刚刚学了类与对象,所以我在这道题里就根据学过的写了一个日期类,并且把题中所需要的函数都写了出来,所以这道题我写的可能会比较多一点。但是后面的题就只是为了做题,而不是练习了。

        这道题要求我们求两个日期的差值(不计正负号),那么我们可以让小的日期往上加,同时定义一个计天数的变量,让这个变量也++,这样等到小日期加到了大日期,就得到了差值。如果我们不知道哪个日期大哪个日期小,那么我们可以先设定一个是大的日期,如果它不是,再更改

(3)代码

#include <iostream>

using namespace std;

class Date
{
public:
    Date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

    //判断是否是闰年
    bool isLeapYear(int year)
    {
        if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
        {
            return true;
        }

        return false;
    }

    //得到某一年某一月的天数
    int GetMonthDay(int year, int month)
    {
        int a[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (month == 2 && isLeapYear(year))
        {
            return 29;
        }
        else
        {
            return a[month];
        }
    }

    //操作符>重载
    bool operator>(const Date& d) const
    {
        if (_year > d._year
            || _year == d._year && _month > d._month
            || _year == d._year && _month == d._month && _day > d._day)
        {
            return true;
        }

        return false;
    }

    //操作符!=重载
    bool operator!=(const Date& d) const
    {
        return !(_year == d._year && _month == d._month && _day == d._day);
    }

    //操作符前置++重载
    Date& operator++()
    {

        ++_day;
        if (_day > GetMonthDay(_year, _month))
        {
            _day -= GetMonthDay(_year, _month);
            ++_month;
            if (_month == 13)
            {
                _month = 1;
                ++_year;
            }
        }
        return *this;
    }

    int Date_Sub(const Date& d);
private:
    int _year;
    int _month;
    int _day;
};

int Date::Date_Sub(const Date& d)
{
    Date max = *this;
    Date min = d;
    int count = 0;

    if (d > *this)
    {
        max = d;
        min = *this;
    }
    while (max != min)
    {
        ++min;
        ++count;
    }
    return count;
}

int main()
{
    int year, month, day;
    int date;
    cin >> date;

    //根据输入样例,得到年、月、日
    year = date / 10000;
    month = date / 100 % 100;
    day = date % 100;
    Date d1(year, month, day);

    cin >> date;
    year = date / 10000;
    month = date / 100 % 100;
    day = date % 100;
    Date d2(year, month, day);
    int d = d2.Date_Sub(d1);
    printf("%d", d + 1);

    return 0;
}

2.计算日期到天数转换_牛客题霸_牛客网

(1)描述:

根据输入的日期,计算是这一年的第几天。

保证年份为4位数且日期合法。

输入描述:

输入一行,每行空格分割,分别是年,月,日

输出描述:

输出是这一年的第几天

示例1

输入:

2012 12 31

输出:

366

示例2

输入:

1982 3 4

输出:

63

(2)思路

        一年只有365或者366天(只需要判断一下是否为闰年),因此我们可以创建一个数组去存到每一个月时,过去了多少天,然后根据输入的数据,就可以转换为天数。

(3)代码 

#include <iostream>

using namespace std;

int main()
{
    int dayArray[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
    int year, month, day;
    cin >> year >> month >> day;
    
    //转换为天数
    int d = dayArray[month - 1] + day;

    //判断是否为闰年
    if((month > 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
    {
        d += 1;
    }
    cout << d;

    return 0;
}

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

(1)描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0  < n ≤ 200

示例1

输入:

5

返回值:

15

示例2

输入:

1

复制返回值:

1

(2)思路

        我们不能使用这些运算符,但是我们学过构造函数,每次创建类对象时都会进入构造函数,这时我们再创建静态成员变量,这样就可以达到+的操作了。

(3)代码 

class Sum
{
public:
    //通过构造函数去进行+的操作
    Sum()
    {
        _ret += _i;
        ++_i;
    }
    
    //静态成员要使用静态成员函数
    static int GetRet()
    {
        return _ret;
    }
private:
    static int _i;
    static int _ret;
};
 
int Sum::_i = 0;
int Sum::_ret = 0;
 
class Solution {
public:
    int Sum_Solution(int n) {
        //利用构造函数
        Sum* a = new Sum[n];
         
        //通过匿名对象返回
        return Sum().GetRet();
    }
};

也可以采用内部类的方式:

class Solution {
private:
//内部类
    class Sum
{
public:
    Sum()
    {
        _ret += _i;
        ++_i;
    }
};
     
public:
    int Sum_Solution(int n) {
        Sum* a = new Sum[n];
         
        return _ret;
    }
private:
    static int _i;
    static int _ret;
};
 
int Solution::_i = 1;
int Solution::_ret = 0;

4.打印日期_牛客题霸_牛客网

(1)描述

给出年分m和一年中的第n天,算出第n天是几月几号。

输入描述:

输入包括两个整数y(1<=y<=3000),n(1<=n<=366)。

输出描述:

可能有多组测试数据,对于每组数据, 按 yyyy-mm-dd的格式将输入中对应的日期打印出来。

示例1

输入:

2000 3
2000 31
2000 40
2000 60
2000 61
2001 60

输出:

2000-01-03
2000-01-31
2000-02-09
2000-02-29
2000-03-01
2001-03-01

(2)思路

        这个类似于第2题的逆置。我们想知道加上所给的天数后,会到这一年的哪一天。那么我们就可以创建到达每个月时的天数,又因为是多组输入,所以我们这里再多创建两个数组,为了让下面在输入时,根据输入的年份不同,随时更改天数。

        接下来,我们用给的天数从后往前(从一年的总天数开始)依次与到达某一月的天数进行比较,比较到所给的天数>的第一个月份之前的总天数,就是结果month,然后再用所给总天数-结果month之前所有天数,就可以得到结果day

(3)代码 

#include <iostream>
 
using namespace std;
int main()
{
    int dayArray[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
    int dayArrayLeap[13] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
    //创建一个一直是平年的数组,为了防止dayArray在下面被改后,无法恢复
    int save[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
    int y, n;
    int m = 1;
    int d = 0;
    int flag = 0;
    while (cin >> y >> n)
    {
        //判断是否为闰年
        if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0))
        {
            //闰年dayArray就是DayArrayLeap
            for (int i = 0; i <= 12; ++i)
            {
                dayArray[i] = dayArrayLeap[i];
            }
        }
        else
        {
            //平年dayArray恢复
            for (int i = 0; i <= 12; ++i)
            {
                dayArray[i] = save[i];
            }
        }
 
        //天数从一年的总天数开始,到哪个进入if就是哪个月
        for (int i = 12; i >= 1; --i)
        {
            if (n > dayArray[i])
            {
                m = i + 1;
                d = n - dayArray[i];
                flag = 1;
                break;
            }
        }
        
        //特殊情况,小于第一个月的天数,无法进入上面if中
        if (flag == 0)
        {
            d = n;
        }
        
        //按照输入格式输出
        printf("%04d-%02d-%02d\n", y, m, d);
    }
 
    return 0;
}

5.日期累加_牛客题霸_牛客网

(1)描述

设计一个程序能计算一个日期加上若干天后是什么日期。

输入描述:

输入第一行表示样例个数m,接下来m行每行四个整数分别表示年月日和累加的天数。

输出描述:

输出m行,每行按yyyy-mm-dd的个数输出。

示例1

输入:

1
2008 2 3 100

输出:

2008-05-13

(2)思路

        这个是一个日期加上一个天数,求加上之后的日期。我们可以先让当前天与所给天加在一起,之后通过循环,依次让day减掉每一个月的天数同时++月,直到day是这个月的合理值时停止

(3)代码 

#include <iostream>
#include <stdio.h>

using namespace std;

int main()
{
    int dayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    int m;
    cin >> m;
    int year, month, day, nd;
    for (int i = 0; i < m; ++i)
    {
        cin >> year >> month >> day >> nd;
        //总天数
        day += nd;

        //day是当前月的合理值时,停止循环
        while (day > dayArray[month])
        {
            //是否为闰年
            if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
            {
                dayArray[2] = 29;
            }
            else
            {
                dayArray[2] = 28;
            }
            day -= dayArray[month];
            ++month;
            
            //月份到13,就++year,month变为1
            if (month == 13)
            {
                ++year;
                month = 1;
            }
        }

        printf("%04d-%02d-%02d\n", year, month, day);
    }
}

二.选择题

1. 关于引用以下说法错误的是(E)

A.引用必须初始化,指针不必

B.引用初始化以后不能被改变,指针可以改变所指的对象

C.不存在指向空值的引用,但是存在指向空值的指针

D.一个引用可以看作是某个变量的一个“别名”

E.引用传值,指针传地址

F.函数参数可以声明为引用或指针类型

解释:

E:引用表面好像是传值,其本质也是传地址,只是这个是编译器来完成的

2.关于c++的inline关键字,以下说法正确的是(A)

A.使用inline关键字的函数会被编译器在调用处展开

B.头文件中可以包含inline函数的声明

C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数

D.递归函数也都可以成为inline函数

解释:

A:不一定会展开,因为inline只是一种建议,此函数是否能够成为内联函数是编译器来根据函数大小来决定的

B:inline函数的声明不能放在头文件,需要放在源文件中

C:inline函数会在调用的地方展开,所以符号表中不会有inline函数的符号名不存在链接冲突,因此可以

D:递归属于比较长的函数,递归函数就算定义为inline,也会被编译器忽略

3. 以下代码共调用多少次拷贝构造函数: (D)

Widget f(Widget u)
{  

  Widget v(u);

  Widget w=v;

  return w;

}

int main(){

  Widget x;

  Widget y=f(f(x));

}

A.1

B.3

C.5

D.7

解释:

        这里涉及到隐式类型转换的问题:连续的构造和拷贝构造或者连续的拷贝构造在一起时,大部分编译器会合二为一,会变成一个拷贝构造。

        这里f(x)值传参调用第1次拷贝构造v(u),w=v调用第2,3次拷贝构造。接下来是值返回,以及进行第二次f(x)值传参正常会调用两次拷贝构造,但是因为是连续的拷贝构造,所以发生隐式类型转换,因此调用第4次拷贝构造。然后v(u),w=v调用第5,6次拷贝构造最后又是值返回,同时又用返回的值进行拷贝构造,这里又是连续的两次拷贝构造发生隐式类型转换调用第7次拷贝构造。因此选D,一共7次。

4. 拷贝构造函数的特点是(D)

A.该函数名同类名,也是一种构造函数,该函数返回自身引用

B.该函数只有一个参数,是对某个对象的引用

C.每个类都必须有一个拷贝初始化构造函数,如果类中没有说明拷贝构造函数,则编译器系统会自动生成一个缺省拷贝构造函数,作为该类的保护成员

D.拷贝初始化构造函数的作用是将一个已知对象的数据成员值拷贝给正在创建的另一个同类的对象

 解释:

A:拷贝构造函数也是一种构造函数,不能有返回值

B:该函数参数是自身类型的对象的引用(不是某个对象,是确定的自身类型的对象)

C:编译器自动生成的缺省拷贝构造函数,是作为该类的公有成员(如果私有就无法进行默认的拷贝构造了)

5. 已知表达式++a中的"++"是作为成员函数重载的运算符,则与++a等效的运算符函数调用形式为(A)

A.a.operator++()

B.a.operator++(0)

C.a.operator++(int)

D.operator++(a,0)

解释:

B、C:传递了整形参数,为后置++

D.参数过多,语法错误

6. 在重载一个运算符为成员函数时,其参数表中没有任何参数,这说明该运算符是 (C)

A.无操作数的运算符

B.二元运算符

C.前缀一元运算符

D.后缀一元运算符

 解释:

类比前置++和后置++,前置++的参数表中没有参数后置++要传递整形参数或int

因此该参数表没有参数就说明是前缀运算符,因此选C

A:重载为成员函数时,其函数的参数个数与真实的函数参数个数会减少1个,减少的则 过this指针进行传递,参数表无参,但有一个隐藏的参数this指针

B:无参成员函数相当于有一个参数的全局函数,不能是二元运算符

7. 假设 AA 是一个类, AA* abc () const 是该类的一个成员函数的原型。若该函数返回 this 值,当用 x.abc ()调用该成员函数后, x 的值是(D)

A.可能被改变

B.已经被改变

C. 受到函数调用的影响

D.不变

 解释:

A、B:此成员函数被定义为const常变量代表在函数内部不能修改任何当前对象的数据成员,因此x不可能改变

C:x的值在函数内部不受任何影响

8. 下列关于赋值运算符“=”重载的叙述中,正确的是(A)

A.赋值运算符只能作为类的成员函数重载

B.默认的赋值运算符实现了“深层复制”功能

C.重载的赋值运算符函数有两个本类对象作为形参

D.如果己经定义了复制拷贝构造函数,就不能重载赋值运算符

 解释:

A: 赋值运算符在类中不显式实现时编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数

B:默认的赋值运算符是按成员赋值,属于浅赋值(浅拷贝)

C:参数只有一个,另一个通过this指针传递

D:两个函数的调用场景不同,相互没有影响

9. 有一个类A,其数据成员如下, 则构造函数中,成员变量一定要通过初始化列表来初始化的是:(B)

class A {

...

private:

   int a;

public:

   const int b;

   float* &c;

   static const char* d;

   static double* e;

};

A.a b c

B.b c

C.b c d e

D.b c d

E.b

F.c

解释:

a是普通的数据成员,可以通过构造函数进行赋值,

b是const成员,const成员只能通过初始化列表初始化

c是指针的引用(c是指针变量的别名),是一种引用,引用成员只能通过初始化列表初始化

d、e是静态成员,只能在类外初始化

因此选B

总结:

1. 普通数据成员可以再初始化列表初始化,也可以通过构造函数进行赋值

2. const、引用、自定义类型(该类没有默认构造函数)成员只能通过初始化列表初始化

3. static静态成员只能再类外初始化

10. 下面程序的运行结果是(D)

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();

	return 0;
}

A.输出1 1

B.程序崩溃

C.编译不通过

D.输出1 随机值

解释:

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

        因为类中声明顺序是先_a2再_a1,因此即使上面初始化列表是先写_a1,后写_a2,也依旧是先初始化_a2,后初始化_a1

        这里_a2是先初始化的,这时_a1未初始化,因此_a2就是随机值,_a1就是1

11. 在一个cpp文件里面,定义了一个static类型的全局变量,下面一个正确的描述是()

A.只能在该cpp所在的编译模块中使用该变量

B.该变量的值是不可改变的

C.该变量不能在类的成员函数中引用

D.这种变量只能是基本类型(如int,char),不能是C++类型

 解释:

A:static限制了变量具有文件域,因此static只能在所在的cpp文件中使用

B:static变量的值是可以被改变的(const不能被改变)

C:可以被正常访问使用,以及通过成员来进行引用

D:静态变量也可以是自定义类型的变量

12. 关于C++类中static 成员和对象成员的说法正确的是(C)

A.static 成员变量在对象构造时生成

B.static 成员函数在对象成员函数中无法调用

C.static 成员函数没有this指针

D.static 成员函数不能访问static成员变量

 解释;

A:static成员变量是在对象生成之前生成

B:普通成员函数是可以调用static函数的

C:static函数属于所有对象共享不具备this指针

D:static函数唯一能够访问的就是static变量或者其他static函数

13. 下面程序段包含4个函数,其中具有隐含this指针的是(D)

int f1();
class T
{
public:
    static int f2();
    private:friend int f3();
    protect:int f4();
};

A.f1

B.f2

C.f3

D.f4

 解释:

全局函数、static、友元函数都不具备this指针

普通成员函数具有因此的this指针

14. 下面有关友元函数与成员函数的区别,描述错误的是(D)

A.友元函数可以让本类和友元类对象调用

B.友元函数和类的成员函数都可以访问类的私有成员变量或者是成员函数

C.类的成员函数是属于类的,调用的时候是通过指针this调用的

D.友元函数是有关键字friend修饰,调用的时候也是通过指针this调用的

解释:

A:友元函数相当于全局函数可以直接被调用

B:友元的目的就是为了访问类的私有数据,成员函数可以直接访问类的私有数据

C:类的成员函数属于类,调用时其内部数据会通过this指针来调用

D:友元函数不具备this指针

三.注意(部分较为简单的选择题的总结)

这里在之前选择题出现过的,就不再说了(可以看选择题的各个选项以及相应的解释)

 1. 命名空间:还可以写成std::xx 和using std::xx来使用标准库的用法,不是必须写using namespace std

 2. 缺省参数:纯C语言,即.c文件,函数不支持缺省参数,C++即.cpp文件支持缺省参数

半缺省不能随便缺省一半,必须从右往左缺省,否则编译出错

3. 重载函数:重载必须是参数列表有所不同(包括个数和类型),函数重载不能依靠返回值的不同来构成重载(因为调用时无法根据参数列表确定调用哪个重载函数),C语言不支持重载

4. 引用与指针的区别:指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名对引用的操作就是对目标变量的操作

5. 指针需要开辟空间,引用不需要开辟空间

6. 在函数代码少、频繁调用情况下适宜采用 inline 定义内联函数。一般内联内联函数是一种建议,如果函数内部包括循环,递归或者 代码量大且复杂,这些函数即使设置了内联函数系统也不会当做内联函数来处理

7. C语言结构体不支持成员函数,但C++结构体支持成员函数。C++的class与struct本质没有区别,唯一区别 在于默认时class的访问属性为私有,而struct默认为公有

8. this指针代表了当前对象,能够区分每个对象的自身数据

9. 静态成员函数没有this指针,只有非静态成员函数才有,且为隐藏指针。非静态成员函数的第一个参数就是隐藏的this指针

10. this指针不存在对象里面,而是存在于栈中(一般是在栈中的寄存器中)

11. this指针可以为空,单纯的对this赋空是不可以的,不过可以强转直接赋空,不过一般不进行这样的操作

12. 构造函数必须与类名相同。构造函数不可以声明返回类型、不能有返回值(void也不行)。构造函数可以用private修饰,只是这样之后就不能直接实例化对象了。构造函数可以带参数,还可以有多个构造函数构成重载

13. 拷贝构造函数参数必须是引用,若为指针,则是普通构造函数,若都没有,则会导致无限递归。

14. 构造顺序是按照语句的顺序进行构造析构是按照构造的相反顺序进行析构

15. .*   ::   sizeof   ?:    .  以上5个运算符不能重载

16. 一个类的友元函数能够访问类的所有成员

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰果滴

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

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

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

打赏作者

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

抵扣说明:

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

余额充值