文章目录
- C/C++笔试练习
- 选择部分
- (1)多态的概念
- (2)虚函数的概念
- (3)虚表地址
- (4)派生类的虚函数
- (5)虚函数的访问
- (6)分析程序
- (7)指针引用
- (8)动态多态
- (9)分析程序
- (10)分析程序
- 编程题 day16
- 完全数计算
- 扑克牌大小
C/C++笔试练习
选择部分
(1)多态的概念
下列一段 C++ 代码的输出结果是()
#include <iostream>
class Base{
public:
int Bar(char x){
return (int)(x);
}
virtual int Bar(int x){
return (2 * x);
}
};
class Derived : public Base{
public:
int Bar(char x){
return (int)(-x);
}
int Bar(int x){
return (x / 2);
}
};
int main(void)
{
Derived Obj;
Base *pObj = &Obj;
printf("%d,", pObj->Bar((char)(100)));
printf("%d", pObj->Bar(100));
}
A. 100,-100
B. 100,50
C. 200,-100
D. 200,50
答案:B
int main(void)
{
Derived Obj;//1.首先实例化一个子类对象
Base* pObj = &Obj;//2.让父类指针接受子类的地址,形成多态
printf("%d,", pObj->Bar((char)(100)));//3.调用Bar函数(参数是char),直接调用父类的Bar函数
printf("%d,", pObj->Bar(100));//4.调用Bar函数(参数是int),形成多态,调用的是子类的Bar函数
}
(2)虚函数的概念
关于函数的描述正确的是()
A. 虚函数是一个static型的函数
B. 派生类的虚函数与基类的虚函数具有不同的参数个数和类型
C. 虚函数是一个非成员函数
D. 基类中说明了虚函数后,派生类中起对应的函数可以不必说明为虚函数
答案:D
A.虚函数不可以是static。因为static函数保存在静态区中,在静态区中的虚函数没有this指针,就无法找到自身的函数地址,以此来生成虚表。
B. 派生类的虚函数和基类的虚函数应该有相同的参数个数和类型,这样才能保证多态性。 如果派生类的虚函数和基类的虚函数参数不同,那么在调用时会发生错误。
C. 虚函数必须是类的成员函数,不能是非成员函数。同A一样,如果是非成员函数就没有this指针,也无法生成虚表,因此无法实现多态。
D. 在C++中,如果基类中的函数被声明为虚函数,那么派生类中的相应函数默认也是虚函数,不需要显式声明。 这是C++的隐式虚函数。
(3)虚表地址
代码执行后,a和b的值分别为?
class Test{
public:
int a;
int b;
virtual void fun() {}
Test(int temp1 = 0, int temp2 = 0){
a=temp1 ;
b=temp2 ;
}
int getA() {
return a;
}
int getB() {
return b;
}
};
int main()
{
Test obj(5, 10);
// Changing a and b
int* pInt = (int*)&obj;
*(pInt+0) = 100;
*(pInt+1) = 200;
cout << "a = " << obj.getA() << endl;
cout << "b = " << obj.getB() << endl;
return 0;
}
A. 200 10
B. 5 10
C. 100 200
D. 100 10
答案:A
int main()
{
Test obj(5, 10);//1.直接创建Test类型的成员obj,将a=5,b=10
int* pInt = (int*)&obj;//2.使用int类型指针,指向obj对象
*(pInt + 0) = 100;//3.因为有虚函数的生成,所以此时改变的是虚表的地址,将虚表地址赋值为100
*(pInt + 1) = 200;//4.此时将a的值赋值为200,b不变,
cout << "a = " << obj.getA() << endl;//5.打印a,b
cout << "b = " << obj.getB() << endl;
return 0;
}
(4)派生类的虚函数
当一个类的某个函数被说明为virtual,则在该类的所有派生类中的同原型函数_____?
A. 只有 被重新说明时才识虚函数
B. 只有被重新说明为virtual时才是虚函数
C. 都不是虚函数
D. 都是虚函数
答案:D
当一个类的某个函数被说明为virtual时,该函数就成为该类的一个虚函数。在派生类中,如果与基类中的虚函数具有相同的函数原型(即参数个数、类型和顺序相同),则该函数也被视为虚函数,无论是否显式地声明为virtual。
这是因为在派生类中,基类中的虚函数仍然是可访问的,并且可以通过虚函数表进行动态绑定。 因此,在该类的所有派生类中,同原型的函数都是虚函数。因此,答案为D。
(5)虚函数的访问
下面有关虚函数和非虚函数的区别说法错误的是?
A. 子类的指针访问虚函数访问的是子类的方法
B. 子类的指针访问非虚函数访问的是子类的方法
C. 父类的指针访问虚函数访问的是父类的方法
D. 父类的指针访问非虚函数访问的是父类的方法
答案:C
父类的指针访问虚函数时(前提是构成多态的情况下),访问的是子类的方法,而不是父类的方法。这是因为虚函数使用了动态绑定机制,在运行时根据对象的实际类型来确定调用哪个函数。因此,如果父类的指针指向的是子类对象,那么访问虚函数时会调用子类的方法。
其他选项的说法是正确的。子类的指针访问虚函数或非虚函数时,访问的都是子类的方法。父类的指针访问非虚函数时,访问的是父类的方法。 因此,答案为C。
(6)分析程序
下列程序的输出结果:
#include <iostream>
using namespace std;
class A
{
public:
void print(){
cout << "A:print()";
}
};
class B: private A
{
public:
void print(){
cout << "B:print()";
}
};
class C: public B
{
public:
void print(){
A:: print();
}
};
int main()
{
C b;
b.print();
}
A. A:print()
B. B:print()
C. 编译出错
答案:C
class B: private A
B私有继承A,无法访问A中的私有成员函数。
(7)指针引用
以下关于C++的描述中哪一个是正确的()
A. 任何指针都必须指向一个实例
B. 子类指针不可以指向父类实例
C. 任何引用都必须指向一个实例
D. 引用所指向的实例不可能无效
答案:C
A. 在C++中,指针可以被初始化为nullptr或者未初始化, 这两种情况下指针不指向任何实例。此外,指针也可以指向已经被销毁的对象,这种情况下指针同样不指向有效的实例。
B. 在C++中,子类指针可以指向父类实例,这是因为子类继承了父类的所有成员和函数,所以子类指针可以访问父类的所有成员和函数。但是,如果通过子类指针访问父类实例中不存在的成员或函数,会导致编译错误或运行时错误。
C. 在C++中,引用必须在声明时被初始化,且一旦初始化后就不能再改变引用的对象。 因此,引用必须指向一个有效的实例,否则会导致编译错误。
D. 引用所指向的实例可能会变得无效,例如,如果引用所指向的对象被销毁或者引用被重新赋值指向其他对象,那么原来的引用就变得无效了。此外,如果引用所指向的对象是局部变量或临时对象,在对象生命周期结束后,引用也会变得无效。
(8)动态多态
下面关于多态性的描述,错误的是()
A. C++语言的多态性分为编译时的多态性和运行时的多态性
B. 编译时的多态性可通过函数重载实现
C. 运行时的多态性可通过模板和虚函数实现
D. 实现运行时多态性的机制称为动态绑定
答案:C
多态性是指用同一个函数名调用不同的对象,产生不同的结果。多态性分为两种:编译时多态性和运行时多态性。
编译时多态性是通过函数重载(overloading)实现的。函数重载是指在同一作用域内可以定义多个同名的函数,它们具有不同的参数类型或参数个数。 编译器在编译阶段根据调用函数时使用的实参类型和数量,选择对应的函数版本进行调用。
运行时多态性是通过虚函数(virtual functions)实现的。虚函数是基类中声明的函数,它可以在派生类中被重写。通过将基类的指针或引用指向派生类对象,并调用该指针或引用上的虚函数, 程序可以在运行时确定调用哪个类的函数版本。这种机制称为动态绑定(dynamic binding)。
模板是一种编译时多态性,它允许我们使用一种类型不确定的方式编写代码,并在编译时根据实际类型生成对应的代码。
A选项描述了C++中多态性的分类,是正确的。
B选项指出编译时的多态性可以通过函数重载实现,也是正确的。
C选项错误地声称运行时的多态性可以通过模板实现。实际上,模板是编译时多态性的特性。
D选项描述了实现运行时多态性的机制,是正确的。
(9)分析程序
写出下面程序的输出结果
class A
{
public:
void FuncA(){
printf( "FuncA called\n" );
}
virtual void FuncB(){
printf( "FuncB called\n" );
}
};
class B : public A
{
public:
void FuncA(){
A::FuncA();
printf( "FuncAB called\n" );
}
virtual void FuncB(){
printf( "FuncBB called\n" );
}
};
void main( void )
{
B b;
A *pa;
pa = &b;
A *pa2 = new A;
pa->FuncA(); //( 3)
pa->FuncB(); //( 4)
pa2->FuncA(); //( 5)
pa2->FuncB();
delete pa2;
}
A. FuncA called FuncB called FuncA called FuncB called
B. FuncA called FuncBB called FuncA called FuncB called
C. FuncA called FuncBB called FuncAB called FuncBB called
D. FuncAB called FuncBB called FuncA called FuncB called
答案:B
void main(void)
{
B b;
A* pa;
pa = &b;
pa->FuncA(); //1.pa指针指向b对象,此时调用FuncA,不构成多态,打印A
pa->FuncB(); //2.此时父类指针指向子类,"三同"形成多态,调用子类函数,打印BB
A* pa2 = new A;
pa2->FuncA(); //3.父类指针调用父类函数,打印A
pa2->FuncB(); //4.同3,打印B
delete pa2;
}
(10)分析程序
#include<iostream>
using namespace std;
class Base
{
public:
virtual int foo(int x){
return x * 10;
}
int foo(char x[14]){
return sizeof(x) + 10;
}
};
class Derived: public Base
{
int foo(int x){
return x * 20;
}
virtual int foo(char x[10]){
return sizeof(x) + 20;
}
} ;
int main()
{
Derived stDerived;
Base *pstBase = &stDerived;
char x[10];
printf("%d\n", pstBase->foo(100) + pstBase->foo(x));
return 0;
}
在32位环境下,以上程序的输出结果是?
A. 2000
B. 2004
C. 2014
D. 2024
答案:C
int main()
{
Derived stDerived;
Base* pstBase = &stDerived;//1.多态形成
char x[10]; //2.此时父类指针调用foo函数,因为多态,调用子类函数,100*20=200
printf("%d\n", pstBase->foo(100) + pstBase->foo(x));//3.此时父类指针直接调用父类函数,8+10=18,一共是2000+18=2018
return 0; //注意32位下指针是4字节,64位下指针是8字节
}
编程题 day16
完全数计算
完全数计算
解题思路:本题可以通过遍历每一个约数,求和,判断完全数。约数计算可以遍历sqrt(n)的范围。
#include<iostream>
using namespace std;
int Count_Of_Perfect_Number(int n)
{
int count = 0, sum = 0;
for (int i = 2; i <= n; ++i)
{
for (int j = 1; j < i; ++j)
{
if (i % j == 0) //对约数进行求和
sum += j;
}
if (sum == i)
count++;
sum = 0;
}
return count;
}
int main()
{
int n, count;
while (cin >> n)
{
count = Count_Of_Perfect_Number(n);
cout << count << endl;
}
return 0;
}
扑克牌大小
扑克牌大小
解题思路:本题的题目意思是输入的只是这些类型中的一种,个子,对子,顺子(连续5张),三个,炸弹(四个)和对王。其实就是最多5张牌(顺子),最少1一张牌之间的比较。不存在其他情况。由于输入保证两手牌都是合法的,顺子已经从小到大排列,按照题意牌面类型的确定和大小的比较直接可以转换为牌个数的比较。
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string FindMax(const string& line)
{
if (line.find("joker JOKER") != string::npos)
return "joker JOKER";
int dash = line.find('-');
//分开两手牌
string car1 = line.substr(0, dash);
string car2 = line.substr(dash + 1);
//获取两手牌的张数
int car1_cnt = count(car1.begin(), car1.end(), ' ') + 1;
int car2_cnt = count(car2.begin(), car2.end(), ' ') + 1;
//获取两手牌的各自第一张牌
string car1_first = car1.substr(0, car1.find(' '));
string car2_first = car2.substr(0, car2.find(' '));
if (car1_cnt == car2_cnt)
{
//两手牌的类型相同
string str = "345678910JQKA2jokerJOKER";
if (str.find(car1_first) > str.find(car2_first))
return car1;
return car2;
}
if (car1_cnt == 4) //说明是炸弹
return car1;
else if (car2_cnt == 4)
return car2;
return "ERROR";
}
int main()
{
string line, res;
while (getline(cin, line))
{
res = FindMax(line);
cout << res << endl;
}
return 0;
}