一.不定项选择题。
1.下列哪些运算符不能够重载(CE)
A. =(赋值运算符)
B. <(关系运算符)
C. .(成员访问符)
D. ->(指向成员访问符)
E. ::(域运算符)
解析:
c++中不能重载的运算符只有5个:
. (成员访问运算符)
.* (成员指针访问运算符)
:: (域运算符)
sizeof (长度运算符)
?: (条件运算符)
前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征。
2.下面哪种情况情况下,B不能隐式转换为A(B)
A. Class B:public A{}
B.Class A:public B{}
C.Class B{operator A();}
D.Class A{A(const B&);}
解析:
A.A是基类,B是派生类,在公有继承方式下,派生类对象、派生类对象指针、派生类对象引用可以赋值给基类的对象/基类对象指针/基类对象引用(隐式转换)。因为派生类中包含了基类中的所有信息。注意:将派生类对象赋值给基类对象,会造成截切问题(派生类中专属的数据和行为会被丢弃掉)
B.A是派生类,B是基类。基类对象、基类对象指针、基类对象引用不能赋值给派生类的对象、派生类 对象指针、派生类对象引用。
C. operator A()是B类中的一个转换函数,可以将B类对象隐式转换成A类对象。
D.A(const B&)是A类的一个复制构造函数,可以将B类对象隐式转换成B类对象。
3.“new” is c++ is a?(C)
A.library function like malloc in c
B.key word
C.operator
D.none of the above
解析:
new是c++的关键字。
4.“static” in c++ can be used as(ABC)
A.限制变量的作用域;
B.设置变量的存储域;
C.限制函数的访问域;
D.设置编译链接的方式;
解析:
static关键字的作用:
1.设置变量的存储域,静态局部变量的内存只分配一次,因此其值在下次调用的时候仍维持上次的值。
2.限制变量的作用域,在模块内static的全局变量可以被模块内的所有函数访问,但不能被模块外的其他函数访问。
3.限制函数的作用域,在模块内的static的函数可以被模块内的其它函数访问,这个函数的作用域限制在定义它的模块内。
5.调用一成员函数时,使用动态联编的情况是(B)
A.通过对象调用一虚函数
B.通过指针或引用调用一虚函数
C.通过对象调用静态函数
D.通过指针或引用调用一静态函数
解析:
联编又称绑定:是指计算机程序自身彼此关联的过程,也就是把一个函数名和一个函数体联系在一起的过程。
静态联编:在编译过程中,编译系统可以根据类型匹配等特征来确定程序中调用操作与执行某个同名函数实现之间的关系,即确定某一个同名函数到底是要调用哪一段函数实现代码。
函数重载和运算符重载就是通过静态联编方式实现的编译时的多态的体现。
静态联编的优点是函数调用速度快、效率较高,缺点是编程不够灵活。
动态联编:只有在运行程序时才能确定将要调用的函数。这种在运行阶段进行的联编称为动态联编。
动态联编这个机制是由虚函数支持的,即运行时的多态,基类的某个成员函数声明为虚函数,派生类继承,而且同样重写该函数,那么当声明一个派生类的指针或者引用时,它所调用的函数是由该指针指向的对象确定的。
动态联编的优点是提供了更好的编程灵活性、问题抽象性和程序易维护性,缺点是与静态联编相比,函数调用速度慢。
6.对于下面代码,运行后输出结果是(C)
class ClassA
{
public:
ClassA(int i):m_id(i){}
~ClassA(void) {printf("%d",m_id);}
private:
int m_id;
};
ClassA a(1);
int main(int argc,char* argv[])
{
ClassA *b =new ClassA(2);
ClassA c(3);
static ClassA d(4);
delete b;
return 0;
}
A.1 2 3 4
B.1 2 4 3
C.2 3 4 1
D.2 3 1 4
7.对于下面代码,运行后的结果是(A)
class A
{
public:
A()
{
a=1;
b=2;
}
private:
int a;
int b;
};
class B
{
public:
B()
{
c=3;
}
void print()
{
cout<<c;
}
private:
int c;
};
int main(int argc, char* argv[])
{
A a;
B* pb= (B*)(&a);
pb->print();
return 0;
}
A.1
B.2
C.3
D.程序崩溃
解析:
c++支持指针类型强转,因此排除D,pb的地址和&a的值是相同的,因此c的值和a的是相同的。
8.下面会使这段程序编译错误的有(D)
class A
{
public:
A(){}
};
class B:public A
{
public:
B(){}
};
A* pb=new B();
B b;
A. A* pa=dynamic_cast<A *>(pb);
B. A* pa=static_cast<A*>(pb);
C. A a = static_cast<A>(b);
D. A a=dynamic_cast<A>(b);
E.none of above
解析:
C++同时提供了4种新的强制类型转换形式:const_cast(expression)、dynamic_cast(expression)、 reinterpret_cast(expression)和 static_cast(expression),每一种都适用于特定的目的。
static_cast的转换格式:static_cast <type-id> (expression)
将expression转换为type-id类型,用于对基本数据类型间互相转换,以及类层次结构中基类和子类之间指针或引用的转换,不提供运行时的检查来确保转换的安全性。
同时,若非基类和子类的关系,不能进行无关类类型指针之间的转换,还可将任何类型的表达式转换成void*类型。
dynamic_cast的转换格式:dynamic_cast <type-id> (expression)
Type-id 必须是类的指针、类的引用或者void*。
dynamic_cast是运行时处理的,同时,运行时要进行类型检查的功能,比static_cast更安全。并且不能用于内置的基本数据类型的强制转换,dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL,而且使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。
注意:
1.将基类指针指向派生类指针,当基类指针指向派生类对象时,static_cast和dynamic_cast均可以,当基类指针指向基类对象,static_cast可以,但dynamic_cast不可以,返回为null。
2.派生类继承两个基类,当基类1指针指向派生类对象时,将基类1指针转换为基类2指针static_cast不可以,不能编译,但dynamic_cast可以。
const_cast的转换格式:const_cast<type_id> (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
reinterpret_cast的转换格式:reinterpret_cast<type-id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转成一个整数,也可以把一个整数转换成一个指针(先把一个整数转换成一个指针,在把该整数转换成原指针类型指针,还可以得到原先指针指向的值)。
因此,D是正确答案, dynamic_cast 中的类型必须是指向完整类类型或 void * 的指针或引用。
9.以下哪些做法是不正确或者应该极力避免的(ACD)
A. 构造函数声明为虚函数
B.派生关系中的基类析构函数声明为虚函数
C.构造函数调用虚函数
D.析构函数调用虚函数
解析:
正确答案ACD,当基类指针指向new出来的派生类对象时,为了释放派生类动态分配的内存,需要将基类析构函数声明为虚函数,但是构造函数不能声明为虚函数,不然派生类的对象在初始化的时候会调用不了基类的构造函数导致继承的数据没初始化,再者,构造函数和析构函数都是特殊的成员函数,在其中访问虚函数时,c++将采用静态联编,即在构造函数或析构函数内,即使是使用虚函数名的形式来调用,编译器仍将其解释为静态联编“本类名::虚函数名”,因而这样会与使用者的意图不符,应该尽量避免。
10.在面向对象技术中多态性是指(C)
A.一个类可以派生出多个类
B.一个对象在不同的运行环境中可以有不同的变体
C.针对同一请求,不同对象可以以适合自身的方式加以响应
D.一个对象可以由对个其他对象组成
解析:
多态性反映了能够在多于一个类的对象中完成同一事物的能力,即用同一种方法在不同的类中处理不同的对象。
11.假设A为抽象类,下列声明正确的是(B)
A. A fun(int);
B. A*p;
C. int fun(A&);
D. A Obj;
解析:
带有纯虚函数的类称为抽象类。
注意:
1)抽象类只能作为基类使用,其纯虚函数的实现由派生类给出;但派生类
仍可不给出纯虚函数的实现,派生类继续作为抽象类存在。
2)抽象类不能定义对象,一般将该类的构造函数说明为保护的访问控制权限。
3)可以声明一个抽象类的指针和引用。通过指针和引用,可以指向并访问派
生类对象,进而访问派生类的成员,这种访问是具有多态特征的。
12.下列代码的输出结果为(D)
class Cparent
{
public:
void Intro()
{
printf("I'm a Parent, ");
Hobby();
}
virtual void Hobby()
{
printf("I like footbal!");
}
};
class CChild : public Cparent
{
public:
void Intro()
{
printf("I'm a Child, ");
Hobby();
}
virtual void Hobby()
{
printf("I like basketball!");
}
};
int main(void)
{
CChild *pChild = new CChild();
Cparent* pParent = (Cparent*)pChild;
pParent->Intro();
delete pChild;
return 0;
}
A.I'm a Child, I like football!
B.I'm a Child, I like basketball!
C.I'm a Parent, I like football!
D.I‘m a Parent, I like basketball!
13.下列排序算法中,哪些时间复杂度不会超过n*logn(BC)
A.快速排序算法
B.堆排序
C.归并排序
D.冒泡排序
14.一个栈的入栈序列是A ,B ,C ,D ,E,则栈的不可能输出序列是(C)
A.EDCBA
B.DECBA
C.DCEAB
D.ABCDE
解析:
栈是先进后出、边进边出。
在A中,由于入栈顺序是ABCDE,而EDCBA为其入栈的相反顺序,为可取序列;
在B中,先入栈ABCD,取出D,再入栈E,取出E,接下来的顺序依次取出,为可取序列;
在C中,基于入栈顺序是ABCDE,而D第一个出栈,则此时ABC已经入栈,然后C出栈(C比AB后入栈),然后E入栈,然后E出栈,此时剩下AB,B比A后入栈,所以先输出B再输出A,所以C只能为DCEBA,无法为DCEAB;
而对于D,则是边进边出,则序列可为ABCDE。
15.不能用于Linux中的进程通信的有(D)
A.共享内存
B.命名管道
C.信号量
D.临界区
解析:
所谓的临界区(critical section),实际上指的是一段代码。选D;在《Windows核心编程第五版》中,对临界区的解释是:它是一小段代码,它在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。这里的原子方式,指的是代码知道除了当前线程之外,没有其他任何线程会同时访问该资源。当然,系统仍然可以暂停当前线程去调度其他线程。但是,在当前线程离开临界区之前,系统是不会去调度任何想要访问同一资源的其他线程。
至于A、B、C,都是进程通信的手段。
16.两个等价线程并发的执行下列程序,a为全局变量,初始为0,假设printf、++、--操作都是原子性的,则输出不可能是(A)
void fun()
{
if(a<=0)
{
a++;
}
else
{
a--;
}
printf("%d",a);
}
A.01
B.10
C.12
D.22
解析:
对于B答案,P1执行程序,输出1,P2执行程序,输出0;
对于C答案,初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行判断语句,并执行输出,得到1,P1然后继续执行,此时它该执行a++,这时a=1,执行并输出,结果为2;
对于D答案,初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行a++,得到a=1,中断,P1继续执行a++,a=2,P1输出,得到2,P1结束,P2继续执行输出语句,得到2。
17.以下哪些进程状态转换是正确的(D)
A.就绪到运行
B.运行到就绪
C.运行到阻塞
D.阻塞到运行
E.阻塞到就绪
解析:
这题考察linux系统的进程调度问题,A、B、C、E都是可以的。D中,阻塞到运行,中间需要经历就绪状态。
进程调度:
四种进程间的状态转换:
就绪—>执行 执行—>阻塞 阻塞—>就绪 执行—>就绪
1)进程的三种基本状态
进程在运行中不断地改变其运行状态。通常,一个进程必须具有以下三种基本状态:
就绪状态:
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态就称为就绪状态;
执行状态:
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态;
阻塞状态:
正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而进入阻塞状态。引起进程阻塞的事件有很多种,例如,等待I/O完成、申请缓冲区不能满足、等待信号等。
2)进程三种状态间的转换
一个进程在运行期间,不断地从一种状态转换到另一种状态,它可以多次处于就绪状态和执行状态,也可以多次处于阻塞状态。
A. 就绪—>执行
处于就绪状态的进程,当进程调度程序为之分配好了处理机后,该进程便由就绪状态转换为执行状态;
B. 执行—>就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已经用完而不得不让出处理机,于是进程从执行状态转换为就绪状态;
C. 执行—>阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态;
D. 阻塞—>就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程便从阻塞状态转变为就绪状态。
18.IP地址为140.123.0.0的地址是B类地址,若要切割为10个子网,而且都要连接上Internet,请问子网掩码应设为(D)
A.255.0.0.0
B.255.255.0.0
C.255.255.128.0
D.255.255.240.0
解析:
B类地址中,后16位为主机地址,255.255.0.0,二进制为11111111 11111111 00000000 00000000
要想切割成10个子网,至少要向主机位借4位,2^4=16>10
则子网掩码设置成20位,即二进制为 11111111 11111111 11110000 00000000,再换算成十进制之后为:255.255.240.0
IP地址:类似于你这台电脑的标志,但在网络上是靠IP地址识别的。如果利用TCP/IP协议组网,那么一个网段内的所有电脑都必须有一个IP地址,并且不能重复。
子网掩码和IP地址是配合一起的,将IP地址分成两段,网络段和主机段。
例如你的IP地址是192.168.1.2,子网掩码是255.255.255.0,那么子网掩码全是255的对应的IP地址段表示网络段,是0的对应的IP地址段表示主机段,以上为例,则192.168.1表示网络,2表示主机。
如果需要在这个网络内新增一台主机,则只要改变仅也只能改变最后一位。这样才能保证在同一网络。
19.根据规律,横线处的数字应该为?1/2、1、3/4、5/8、_____(略)
A.1/4
B.3/7
C.5/12
D.9/17
二.填空题
1.C++编译器自动为类生成的缺省函数有哪些__1、默认构造函数, 2、拷贝构造函数, 3、赋值函数, 4、析构函数__。
2.已知一颗二叉树,如果先序遍历的节点顺序是ADCEFGHB,中序遍历是:CDFEGHAB,则后序遍历结果为__CFHGEDBA___。
解析:
先序遍历简单记为:根左右
中序遍历简单记为:左根右
后序遍历简单记为:左右根
3.基类的析构函数不是虚函数,会带来什么问题?___不能释放派生类对象可能分配的动态内存____。
4.如下代码的输出结果是___1448___。
class A{};
class B:public virtual A{};
class C:public virtual A{};
class D:public B,public C{};
int main()
{
cout << sizeof(A) << sizeof(B) << sizeof(C) << sizeof(D) << endl;
return 0;
}
5.如下代码的输出结果为__266___。
#include<stdio.h>
union
{
int i;
char x[2];
}a;
void main()
{
a.x[x]=10;
a.x[1]=1;
printf("%d",a.i);
}
纯手打 如有错 请指教!