一、选择题
1、下列关于动态联编的描述中,错误的是()。
A.动态联编是以虚函数为基础
B.动态联编是运行时确定所调用的函数代码的
C.动态联编调用函数操作是指向对象的指针或对象引用
D.动态联编是在编译时确定操作函数的
答案:D
2、设置虚基类的目的是( B )
A.简化程序 B.消除二义性 C.提高程序运行效率 D.减少目标代码
3、在派生类中重新定义虚函数时必须在(ABE)方面与基类保持一致。(多选题)
A.参数个数 B.参数类型 C.参数名字 D.操作内容 E.返回类型
4、多继承派生类构造函数构造对象时,()被最先调用。
A.派生类自己的构造函数 B.虚基类的构造函数
C.非虚基类的构造函数 D.派生类中子对象类的构造函数
答案:B
分析:多继承派生类构造函数构造对象时,构造函数的调顺序是:虚基类的构造函数,
派生类中子对象类的构造函数, 派生类自己的构造函数。
5、C++类体系中,能被派生类继承的是()。
A.构造函数 B.虚函数 C.析构函数 D.友元函数
答案:B
分析:C++类体系中,构造函数、析构函数和友元函数是不能被派生类继承的.
6、关于虚函数的描述中,正确的是()。
A.虚函数是一个静态成员函数
B.虚函数是一个非成员函数
C.虚函数即可以在函数说明定义,也可以在函数实现时定义
D.派生类的虚函数与基类中对应的虚函数具有相同的参数个数和类型
答案:D
7、下面4个选项中,( )是用来声明虚函数的。
A.virtual B.public C.using D.false
答案:A
8、编译时的多态性可以通过使用( )获得。
A.虚函数和指针
B.重载函数和析构函数
C.虚函数和对象
D.虚函数和引用
答案:C,编译时多态就是静态多态,静态多态有函数重载、运算符重载、模板。A与D都可以实现动态多态。
使用对象调用虚函数,是静态联编,也就是静态多态(上课虚函数访问讲过)
9、关于纯虚函数和抽象类的描述中,错误的是( )。
A.纯虚函数是一种特殊的虚函数,它没有具体的实现
B.抽象类是指具有纯虚函数的类
C.一个基类中说明有纯虚函数,该基类派生类一定不再是抽象类
D.抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出
答案:C
10、下列描述中,( )是抽象类的特征。
A.可以说明虚函数
B.可以进行构造函数重载
C.可以定义友元函数
D.不能说明其对象
答案:D
11、以下( )成员函数表示虚函数。
A.virtual int vf(int);
B.void vf(int)=0;
C.virtual void vf()=0;
D.virtual void vf(int) { };
答案:C
12、如果一个类至少有一个纯虚函数,那么就称该类为(A)。
A.抽象类 B.虚函数 C.派生类 D.以上都不对
答案:A
13、要实现动态联编,必须通过( )调用虚函数。
A.对象指针 B.成员名限定 C.对象名 D.派生类名
答案:A
14、下面描述中,正确的是(A)。
A.virtual可以用来声明虚函数
B.含有纯虚函数的类是不可以用来创建对象的,因为它是虚基类
C.即使基类的构造函数没有参数,派生类也必须建立构造函数
D.静态数据成员可以通过成员初始化列表来初始化
答案:A
二、写出下列程序的输出结果。
1、分析下列程序的输出结果。
#include <iostream>
using std::endl;
using std::cout;
class A
{
public:
A()
{
cout << "A's cons." << endl;
}
virtual
~A()
{
cout << "A's des." << endl;
}
virtual
void f()
{
cout<<"A's f()."<<endl;
}
void g()
{
f();
}
};
class B
: public A
{
public:
B()
{
f();
cout<<"B's cons."<<endl;
}
~B()
{
cout<<"B's des."<<endl;
}
};
class C
: public B
{
public:
C() { cout<<"C's cons."<<endl; }
~C() { cout<<"C's des."<<endl; }
void f() { cout<<"C's f()."<<endl; }
};
int main(void)
{
A *a=new C;
a->g();
delete a;
return 0;
}
运行结果:
A’s cons.
A’s f().
B’s cons.
C’s cons.
C’s f().
C’s des.
B’s des.
A’s des.
2、根据给定的程序执行结果,将下列程序补充完整。
#include <iostream>
using std::endl;
using std::cout;
class Base
{
public:
Base(int i)
{
b = i;
}
(1)
protected:
int b;
};
class Derive1
:public Base
{
public:
(2)
void Print()
{
cout<<"Derive1's Print() called."<<endl;
}
};
class Derive2
:public Base
{
(3)
};
void fun( (4) )
{
obj->Print();
}
int main(void)
{
(5)
fun(d1);
fun(d2);
}
程序的执行结果如下:
Derive1's Print() called.
Derive2's Print() called.
#include <iostream>
using std::endl;
using std::cout;
class Base
{
public:
Base(int i)
{
b=i;
}
virtual void Print(){};
protected:
int b;
};
class Derive1
:public Base
{
public:
Derive1()
:Base(1)
{
}
void Print()
{
cout<<"Derive1's Print() called."<<endl;
}
};
class Derive2
:public Base
{
public:
Derive2()
:Base(1)
{}
void Print()
{
cout<<"Derive2's Print() called."<<endl;
}
};
void fun(Base *obj)
{
obj->Print();
}
int main(void)
{
Base *d1=new Derive1;
Base *d2=new Derive2;
fun(d1);
fun(d2);
return 0;
}
3、根据给定的程序执行结果
#include <iostream>
using std::endl;
using std::cout;
class A
{
public:
A(int i,int j)
{
a=i;
b=j;
}
void move(int i,int j)
{
a+=i;
b+=j;
}
void disp()
{
cout << "(" << a << "," << b << ")" << endl;
}
private:
int a,b;
};
class B:public A
{
public:
B(int i,int j,int k,int l)
:A(i,j)
,x(k)
,y(l)
{ }
void disp()
{
cout << x << "," << y << endl;
}
void fun()
{
move(3,5);
}
private:
int x,y;
};
void main()
{
A m(1,2);
m.disp();
B d(3,4,5,6);
d.fun();
d.A::disp();
d.disp();
}
结果:
(1,2)
(6,9)
5,6
4、根据给定的程序执行结果
#include <iostream>
using std::endl;
using std::cout;
class Base {
public:
Base()
{ a=5;
cout<<"Base a="<<a<<endl;
}
protected:
int a;
};
class Base1:virtual public Base{
public:
Base1()
{
a=a+10;
cout<<"Base1 a="<<a<<endl;
}
};
class Base2: virtual public Base{
public:
Base2()
{
a=a+20;
cout<<"Base2 a="<<a<<endl;
}
};
class Derived
:public Base1
,public Base2
{
public:
Derived()
{
cout<<"Derived a="<<a<<endl;
}
};
int main(void)
{
Derived obj;
return 0;
}
运行结果:
Base a=5
Base1 a=15
Base2 a=35
Derived a=35
5、根据给定的程序执行结果
#include<iostream>
using std::endl;
using std::cout;
class Base1{
public:
virtual void fun()
{
cout<<"--Base1--\n";
}
};
class Base2{
public:
void fun()
{
cout<<"--Base2--\n";
}
};
class Derived
:public Base1
,public Base2
{
public:
void fun()
{ cout<<"--Derived--\n"; }
};
int main()
{
Base1 obj1,*ptr1;
Base2 obj2,*ptr2;
Derived obj3;
ptr1=&obj1;
ptr1->fun();
ptr2=&obj2;
ptr2->fun();
ptr1=&obj3;
ptr1->fun();
ptr2=&obj3;
ptr2->fun();
return 0;
}
运行结果:
–Base1–
–Base2–
–Derived–
–Base2–
6、写出下列程序的结果:
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;
}
key:FuncA called
FuncBB called
FuncA called
FuncB called
解析:
B b;
A *pa;
pa = &b;
A *pa2 = newA;
pa->FuncA(); ( 3)//pa=&b动态绑定但是FuncA不是虚函数,所以FuncA called
pa->FuncB(); ( 4)//FuncB是虚函数所以调用B中FuncB,FuncBB called
pa2->FuncA(); ( 5)//pa2是A类指针,不涉及虚函数,调用的都是A中函数,所以FuncA called FuncB called
pa2->FuncB()
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;
}
key:12
Base * pb = new Child(1), 首先创建子类对象,初始化为1;
func1()不是虚函数,所以pb->func1()执行的是基类的func1函数,i= 10,然后调用func2()函数;
这里的func2是虚函数,要往下派生类寻找,找到后执行派生类中的func2(),此时,i = 12;
最后执行pb->getValue(),结果为12
三、简答题
-
什么是多态?虚函数的实现原理是什么?
主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用; -
接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
-
不能设置为虚函数的函数有哪些?构造函数能设置为虚函数吗?为什么?
不能设置为虚函数的函数有:
(1)普通函数(非成员函数)
(2)友元函数
(3)构造函数
(4)静态成员函数
(5)内联函数
构造函数不能被设置为虚函数:
(1)构造函数发生的时机在编译阶段,虚函数发生的时机在运行阶段
(2)构造函数不能被继承,虚函数可以被继承
(3)如果将构造函数设置为虚函数,那么就需要通过虚表找到虚函数的入口地址,那么就需要虚函数指针指向虚表,而虚函数指针存在对象布局的最前面,而如果构造函数不调用,那么对象就不一定是完整的,那么对象的存储布局的前面就不一定有虚函数指针,那如果没有虚函数指针就不能指向虚表。 -
在什么情况下析构函数要设置成虚函数?为什么?
为了解决内存泄漏的问题,所以需要将析构函数设置为虚函数。 -
什么是纯虚函数?什么是抽象类?抽象类的作用是什么?
答:抽象类是含有纯虚函数的类,其作用是作为多个表象不同,但本质相同类的抽象。
故抽象类仅可以作为基类被继承,不可以实例化生成对象,不能初始化,不能被当做返回值,不能当做参数,但可以做指针类型和引用 -
什么是重载?什么是隐藏?什么是覆盖?他们之前的区别是?
答:成员函数被重载特征是:
(1)相同的范围(在同一个类中);
(2)函数名字相同
(3)参数不同
(4)virtual 关键字可有可无
覆盖就是指派生类函数覆盖基类virtual函数,特征是:
(1)不同的范围(分别位于派生类与基类)
(2)函数名字相同
(3)参数相同
(4)基类函数必须有virtual 关键字
“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,派生对象都是调用派生类的同名函数。规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同,此时不论有无virtual关键字、基类的函数将被隐藏;
(2)如果派生类的函数与基类的函数同名,并且参数也相同、但是基类函数没有virtual 关键字,此时基类的函数被隐藏(注意别与覆盖混淆);
总结:
①同一类中的同名函数是重载;
②不同类中同名函数可能是覆盖,也可能是隐藏。根据是否有virtual以及函数参数是否相同区分;