目录
一、选择题
二、编程题
三、选择题题解
四、编程题题解
一、选择题
1、下列有关this指针使用方法的叙述正确的是()
A. 保证基类保护成员在子类中可以被访问
B. 保证基类私有成员在子类中可以被访问
C. 保证基类共有成员在子类中可以被访问
D. 保证每个对象拥有自己的数据成员,但共享处理这些数据的代码
2、有一个类B继承自类A,他们数据成员如下:则构造函数中,成员变量一定要通过初始化列表来初始化的是____。
class A
{
private:
int a;
};
class B : public A
{
private:
int a;
public:
const int b;
A &c;
static const char* d;
B* e;
}
A. a b c
B. b c e
C. b c d e
D. c e
E. b d
F. b c
3、下面说法正确的是()
A. C++已有的任何运算符都可以重载
B. const对象只能调用const类型成员函数
C. 构造函数和析构函数都可以是虚函数
D. 函数重载返回值类型必须相同
4、下面说法正确的是()
A. 一个空类默认一定生成构造函数,拷贝构造函数,赋值操作符,引用操作符,析构函数
B. 可以有多个析构函数
C. 析构函数可以为virtual,可以被重载
D. 类的构造函数如果都不是public访问属性,则类的实例无法创建
5、面向对象设计中的继承和组合,下面说法错误的是?()
A. 继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复用,也称为白盒复用
B. 组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动态复用,也称为黑盒复用
C. 优先使用继承,而不是组合,是面向对象设计的第二原则
D. 继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封装性的表现
6、关于重载和多态正确的是
A. 如果父类和子类都有相同的方法,参数个数不同,将子类对象赋给父类后,由于子类继承于父类,所以使用父类指针 调用父类方法时,实际调用的是子类的方法
B. 选项全部都不正确
C. 重载和多态在C++面向对象编程中经常用到的方法,都只在实现子类的方法时才会使用
D.
class A{ void test(float a){cout<<"1";} };
class B:public A{ void test(int b){cout<<"2";} };
A *a=new A;
B *b=new B;
a=b; a.test(1.1);
结果是1
7、以下程序的输出是()
class Base
{
public:
Base(int j)
: i(j)
{}
virtual~Base() {}
void func1()
{
i *= 10;
func2();
}
int getValue()
{
return i;
}
protected:
virtual void func2()
{
i++;
}
protected:
int i;
};
class Child: public Base
{
public:
Child(int j): Base(j) {}
void func1()
{
i *= 100;
func2();
}
protected:
void func2()
{
i += 2;
}
};
int main()
{
Base * pb = new Child(1);
pb->func1();
cout << pb->getValue() << endl;
delete pb;
return 0;
}
A. 11
B. 101
C. 12
D. 102
8、下面 C++ 代码的运行结果为()
#include<iostream>
#include<string>
using namespace std;
class B0
{
public:
virtual void display()
{
cout << "B0::display0" << endl;
}
};
class B1 :public B0
{
public:
void display() { cout << "B1::display0" << endl; }
};
class D1 : public B1
{
public:
void display()
{
cout << "D1::display0" << endl;
}
};
void fun(B0 ptr)
{
ptr.display();
}
int main()
{
B0 b0;
B1 b1;
D1 d1;
fun(b0);
fun(b1);
fun(d1);
return 0;
}
A. B0::display0 B0::display0 B0::display0
B. B0::display0 B0::display0 D1::display0
C. B0::display0 B1::display0 D1::display0
D. B0::display0 B1::display0 B1::display0
9、下列哪种函数可以定义为虚函数()
A. 构造函数
B. 析构函数
C. 内联成员函数
D. 静态成员函数
10、下面 C++ 程序的运行结果为()
#include<iostream>
using namespace std;
class A
{
public:
A(const char* s) { cout << s << endl; } ~A() {}
};
class B : virtual public A
{
public:
B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class C : virtual public A
{
public:
C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class D : public B, public C
{
public:
D(const char* s1, const char* s2, const char* s3, const char* s4)
:B(s1, s2)
, C(s1, s3)
, A(s1)
{
cout << s4 << endl;
}
};
int main()
{
D* p = new D("class A", "class B", "class C", "class D");
delete p;
return 0;
}
A. class A class B class C class D
B. class D class B class C class A
C. class D class C class B class A
D. class A class C class B class D
二、编程题
1、计算日期到天数转换 题目链接
2、幸运的袋子 题目链接
三、选择题题解
1、下列有关this指针使用方法的叙述正确的是()
A. 保证基类保护成员在子类中可以被访问
B. 保证基类私有成员在子类中可以被访问
C. 保证基类共有成员在子类中可以被访问
D. 保证每个对象拥有自己的数据成员,但共享处理这些数据的代码
正确答案:D
题解:
D选项描述正确,概念题;
2、有一个类B继承自类A,他们数据成员如下:则构造函数中,成员变量一定要通过初始化列表来初始化的是____。
class A
{
private:
int a;
};
class B : public A
{
private:
int a;
public:
const int b;
A &c;
static const char* d;
B* e;
}
A. a b c
B. b c e
C. b c d e
D. c e
E. b d
F. b c
正确答案:F
题解:
必须在初始化列表中进行初始化的有以下,1、const修饰的普通成员变量;2、引用对象;3、无默认构造的类成员;不难选出答案F;
3、下面说法正确的是()
A. C++已有的任何运算符都可以重载
B. const对象只能调用const类型成员函数
C. 构造函数和析构函数都可以是虚函数
D. 函数重载返回值类型必须相同
正确答案:B
题解:
A选项,C++中有五种无法被重载的运算符,分别为 .(点) .*(点星) ::(作用域) sizeof(计算大小) :? (三目); 因此A错误;B选项,const修饰函数即修饰this指针,假设有一个类叫A,则默认this指针的类型为 A* const this,用const修饰后,为 const A* const this;如果不用const修饰函数,const对象会发生权限放大;因此,const对象只能调用const修饰的成员函数,故B正确;C选项,构造函数不能使虚函数,因此虚表在构造函数的初始化列表中初始化,可如果将构造函数设置为虚函数的话,此时虚表都不存在,virtual修饰的构造函数又放哪呢,故C错误;D选项,函数重载要求函数名相同,函数参数个数或参数类型不同,故D错误;
4、下面说法正确的是()
A. 一个空类默认一定生成构造函数,拷贝构造函数,赋值操作符,引用操作符,析构函数
B. 可以有多个析构函数
C. 析构函数可以为virtual,可以被重载
D. 类的构造函数如果都不是public访问属性,则类的实例无法创建
正确答案:A
题解:
A选项,描述正确;B选项,析构函数不能重载,因此只能有一个;C选项,析构函数可以用virtual修饰,可以在子类中被重写,跟重载并无任何关系;D选项,此时可以提供一个公有的静态成员函数,在静态成员函数内调用构造函数实例化出对象;
5、面向对象设计中的继承和组合,下面说法错误的是?()
A. 继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复用,也称为白盒复用
B. 组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动态复用,也称为黑盒复用
C. 优先使用继承,而不是组合,是面向对象设计的第二原则
D. 继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封装性的表现
正确答案:C
题解:
C选项,有限使用组合,而不是继承,因为一个项目工程尽量做到高内聚,低耦合,因此优先使用组合;
6、关于重载和多态正确的是
A. 如果父类和子类都有相同的方法,参数个数不同,将子类对象赋给父类后,由于子类继承于父类,所以使用父类指针 调用父类方法时,实际调用的是子类的方法
B. 选项全部都不正确
C. 重载和多态在C++面向对象编程中经常用到的方法,都只在实现子类的方法时才会使用
D.
class A{ void test(float a){cout<<"1";} };
class B:public A{ void test(int b){cout<<"2";} };
A *a=new A;
B *b=new B;
a=b; a.test(1.1);
结果是1
正确答案:B
题解:
A选项,只有实现了多态,才可能使用不同的对象调用不同的方法,很明显,描述不符合多态的要求,多态有两个要求:1、虚函数的重写;2、用父类的指针或引用调用;故A错误;C选项,重载跟子类没有任何关系;D选项,这里有两处语法错误,1是A类对象的test函数默认为私有,无法调用;2是调用对象处应该用箭头而不应该用点,因为a是一个A类对象的指针;
7、以下程序的输出是()
class Base
{
public:
Base(int j)
: i(j)
{}
virtual~Base() {}
void func1()
{
i *= 10;
func2();
}
int getValue()
{
return i;
}
protected:
virtual void func2()
{
i++;
}
protected:
int i;
};
class Child: public Base
{
public:
Child(int j): Base(j) {}
void func1()
{
i *= 100;
func2();
}
protected:
void func2()
{
i += 2;
}
};
int main()
{
Base * pb = new Child(1);
pb->func1();
cout << pb->getValue() << endl;
delete pb;
return 0;
}
A. 11
B. 101
C. 12
D. 102
正确答案:C
题解:
首先,我们观察,func1是不虚函数,func2是虚函数,并完成了重写;我们new 一个子类对象并将x初始化为1,然后用基类指针指向创建出来的对象,我们用父类指针调用func1时,由于不构成多态,因此调用的是父类的func1,将x*=10 ;此时x等于10;然后我们在func1中调用func2,这是其实我们用隐藏的this指针调用func2的,并且this指针为父类指针,此时构成重载,调用子类的func2,对x+=2;因此我们在打印x时,答案为12;
8、下面 C++ 代码的运行结果为()
#include<iostream>
#include<string>
using namespace std;
class B0
{
public:
virtual void display()
{
cout << "B0::display0" << endl;
}
};
class B1 :public B0
{
public:
void display() { cout << "B1::display0" << endl; }
};
class D1 : public B1
{
public:
void display()
{
cout << "D1::display0" << endl;
}
};
void fun(B0 ptr)
{
ptr.display();
}
int main()
{
B0 b0;
B1 b1;
D1 d1;
fun(b0);
fun(b1);
fun(d1);
return 0;
}
A. B0::display0 B0::display0 B0::display0
B. B0::display0 B0::display0 D1::display0
C. B0::display0 B1::display0 D1::display0
D. B0::display0 B1::display0 B1::display0
正确答案:A
题解:
此题主要考察多态,我们可以看到三个类的对象中的display都为虚函数,并且完成了重写;但是多态还需要用父类的指针或引用调用,而题目中的fun函数是用父类对象调用,因此不构成多态,都调用父类的fun函数,故选A;
9、下列哪种函数可以定义为虚函数()
A. 构造函数
B. 析构函数
C. 内联成员函数
D. 静态成员函数
正确答案:B
题解:
A选项,构造函数不能定义为虚函数,前面解释过,这里就不解释了;B选项,析构函数可以定义为虚函数;C选项,内联函数是在调用出直接展开,不会压栈;因此内联函数不能定义为虚函数;D选项,静态成员没有this指针,所以无法定义为虚函数;
10、下面 C++ 程序的运行结果为()
#include<iostream>
using namespace std;
class A
{
public:
A(const char* s) { cout << s << endl; } ~A() {}
};
class B : virtual public A
{
public:
B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class C : virtual public A
{
public:
C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class D : public B, public C
{
public:
D(const char* s1, const char* s2, const char* s3, const char* s4)
:B(s1, s2)
, C(s1, s3)
, A(s1)
{
cout << s4 << endl;
}
};
int main()
{
D* p = new D("class A", "class B", "class C", "class D");
delete p;
return 0;
}
A. class A class B class C class D
B. class D class B class C class A
C. class D class C class B class A
D. class A class C class B class D
正确答案:A
题解:
这题考察菱形虚拟继承,仔细阅读题目,不难发现,本题就是菱形虚拟继承,此时A中的数据成员只会保存一份,而B类与C类中会分别有一个虚表指针,指向各自的虚表,其中储存A类数据成员的偏移量;再回到题目,我们需要清楚的是,初始化列表中初始化的顺序只与继承或声明顺序有关,因此首先会调用D类的构造函数,其中初始化列表会先调用A类构造函数,然后调用B类的构造函数,接着调用C类构造函数,最后打印D;故选A;
四、编程题题解
1、计算日期到天数转换
思路:我们创建一个数组,数组中保存到每一个月所需要的天数,然后我们通过数组得到month月之前所有的天数,然后加上当前天数即可(平闰年需要特别注意)
#include <iostream>
using namespace std;
// 判断平闰年
bool is_leap(int year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int main()
{
int year, month, day;
cin >> year >> month >> day;
// 天数数组
int monthDays[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
// 得到month月之前所有天数
int ret = monthDays[month - 1];
ret += day;
// 闰年特殊判断
if(is_leap(year) && month > 2)
ret += 1;
cout << ret << endl;
return 0;
}
2、幸运的袋子
思路:DFS+剪枝+回溯,本题可看作一道排列组合题目,首先分析题意,我们根据数论可以得出袋子中必定有1,然后题目是要求我们组合出幸运袋子的个数,我们可以往袋子里依次放求,组合出不同的情况,以下为图解;
以上画出了部分的图,这种组合类似一种树状结构,我们通过这种深度遍历+剪枝+回溯很快可以找出所有情况;具体看代码;
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int get_lucky_bag(vector<int>& bag, int pos, int sum, int multi)
{
// 统计到当前栈帧幸运袋子个数
int count = 0;
// 从pos位置开始往后加
for(int i = pos; i < bag.size(); i++)
{
sum += bag[i];
multi *= bag[i];
// 判断当前是否为幸运袋子
if(sum > multi)
{
// 递归下一个位置 + 当前位置个数
count += 1 + get_lucky_bag(bag, i + 1, sum, multi);
}
else if(bag[i] == 1)
{
// 当前为1且不为幸运袋子,特殊处理往下继续递归
count += get_lucky_bag(bag, i + 1, sum, multi);
}
else
{
// 因为排过序,这个位置的球序号都不是,后面比这大的更不是,进行剪枝
break;
}
// 上层递归结束,进行回溯,把和与乘积还原
sum -= bag[i];
multi /= bag[i];
// 去重工作,因此题目说过相同序号的球是无区别的
while(i + 1 < bag.size() && bag[i] == bag[i + 1])
i++;
}
return count;
}
int main()
{
int n;
cin >> n;
// 将数据放入vector中
vector<int> v(n, 0);
for(int i = 0; i < n; i++)
{
cin >> v[i];
}
// 排序,方面后续剪枝
sort(v.begin(), v.end());
// 参数分别为数据数组,从什么位置开始,和,积
cout << get_lucky_bag(v, 0, 0, 1);
}