笔试强训错题总结(二)
选择题
- 下列哪一个是析构函数的特征()
A. 析构函数定义只能在类体内
B. 一个类中只能定义一个析构函数
C. 析构函数名与类名不同
D. 析构函数可以有一个或多个参数
析构函数可以在类中声明,类外定义,一个类只能有一个析构函数,函数名为
~类名
,不能有参数所以这题选:B
- 若MyClass是一个类名,其有如下语句序列:
MyClass c1,*c2; MyClass *c3=new MyClass; MyClass &c4=c1;
上面的语句序列调用构造函数个数是( )
A. 1 B. 2 C. 3 D. 4
只有在创建对象的时候才会调用构造函数,上述代码中c1和c3创建了对象,所以只会调用两次构造函数。
本题选:B
- 如果有一个类是 myClass , 关于下面代码正确描述的是:
myClass::~myClass() { delete this; this = NULL; }
A.正确,我们避免了内存泄漏
B. 它会导致栈溢出
C. 无法编译通过
D. 这是不正确的,它没有释放任何成员变量。
这段代码有好几个错误:
1.析构函数是用于对象销毁时,清理对象的资源的,但是并不是所有的类都申请了资源
2.this是const类型,不可更改,所以
this=NULL
这条语句无法通过编译3.delete this一定会让程序崩溃
综上所述,这题选:C
- C++中关于堆和栈的说法,哪个是错误的()
A. 堆的大小仅受操作系统的限制,栈的大小一般较小
B. 在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题
C. 堆和栈都可以静态分配
D. 堆和栈都可以动态分配
堆的大小只受操作系统的限制(主要取决于操作系统在进程分配时对内存块如何布局),堆的一般比较大(大小在GB级别),栈一般都比较小(大小在MB级别),如果频繁的调用malloc/new在堆上频繁申请小的内存块就会有内存碎片的产生(可以通过内存池来减少碎片),静态分配是指在编译阶段就可以确定需要开辟多大的空间,堆无法做到这点,堆只能动态分配,栈既可以动态分配又可以静态分配,通过
alloca
函数就可以在栈上动态申请空间。所以这题选:C
- 当一个类对象的生命周期结束后,关于调用析构函数的描述正确的是()
A. 如果派生类没有定义析构函数,则只调用基类的析构函数
B. 如果基类没有定义析构函数,则只调用派生类的析构函数
C. 先调用派生类的析构函数,后调用基类的析构函数
D. 先调用基类的析构函数,后调用派生类的析构函数
析构函数作为六大默认成员函数,就算我们不去显示的定义,编译器也会自动生成析构函数。而调用析构函数的规则就是先调用派生类的析构,再调用基类的析构函数。
所以这题选:C
- 以下关于纯虚函数的说法,正确的是()
A. 声明纯虚函数的类不能实例化
B. 声明纯虚函数的类成虚基类
C. 子类必须实现基类的
D. 纯虚函数必须是空函数
有纯虚函数的类被称为抽象类,不能实例化对象,如果子类不对父类的纯虚函数进行重写,那么子类也是抽象类(也就是说子类可以不对父类重写),此外纯虚函数并不是只能为空函数:
所以这题选:A
- 下列描述,正确的一共有多少个()
1)const char *p,这是一个常量指针,p的值不可修改
2)在64位机上,char *p= “abcdefghijk”; sizeof§大小为12
3)inline会检查函数参数,所以调用开销显著大于宏
4)重载是编译时确定的,虚函数是运行时绑定的
A. 1 B. 2 C. 3 D. 4
首先第一个const修饰的是
* p
也就是说,指针p的值可以更改,但是*p
的值不能更改;p是一个指针,对于64位操作系统而言指针的大小是八个字节,对于32位系统而言指针的大小是四个字节
inline和宏都是在调用的地方展开的,所以它们的调用开销其实是相当的,但是宏没有类型安全的检查,而inline有检查
只有第四个是正确,
所以本题选:A
- 下面说法正确的是()
A. 一个空类默认一定生成构造函数,拷贝构造函数,赋值操作符,引用操作符,析构函数
B. 可以有多个析构函数
C. 析构函数可以为virtual,可以被重载
D. 类的构造函数如果都不是public访问属性,则类的实例无法创建
一个空类也具有六大默认成员函数(构造,析构,拷贝构造,赋值重载,普通对象取地址重载,const对象取地址重载);一个类只能有一个析构函数,析构函数可以为虚函数,可以被重写,但是不能被重载。如果一个类的构造函数是私有的,那么它只能通过公有成员接口来实例化对象;
所以本题选:A
- 以下程序的输出是()
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; }
A. 11 B. 101 C. 12 D. 102
func1函数不是虚函数,所以调用只看类型,因此调用的是父类的func1函数,执行
i*=10
,接下来调用func2函数,因为func2函数是虚函数且被重写,是多态调用(按对象调用),所以调用Child类的func2函数,执行i+=2
;所以i=1*10+2=12
,本题选:C
- 下面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); }
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
首先要明确fun是一个全局函数,它的参数是一个父类对象,而多态的前提是必须要是父类的指针或者引用,所以这里并不构成多态,按类型调用函数,因为是父类的对象,所以都调用的是父类的display函数;
所以本题选:A
- 下列哪种函数可以定义为虚函数()
A. 构造函数 B. 析构函数 C. 内联成员函数 D. 静态成员函数
构造函数是肯定不可以被定义为虚函数的,因为对象都还没完成实例化,而析构函数建议被定义成析构函数,详情可以看:三大特性之多态,内联函数无法被定义为虚函数,因为内联函数是在调用的地方直接展开的,没有地址,也就无法填充虚函数表;虚函数的调用是通过this指针的,静态成员函数没有this指针;
所以本题选:B
- 下面关于虚函数的描述,错误的是
A. 在成员函数声明的前面加上virtual修饰,就可把该函数声明为虚函数
B. 基类中说明了虚函数后,派生类中对应的函数也必须说明为虚函数
C. 虚函数可以是另一个类的友元函数,但不能是静态成员函数
D. 基类中说明的纯虚函数在其任何需要实例化的派生类中都必须实现
基类中将该函数声明成虚函数以后,即使派生类不将该函数声明成虚函数,编译器也会将其认为是虚函数;而且重写不是强制要求的,派生类可以不重写基类的虚函数;
所以本题选:B
- 下面这段代码的执行结果:
class A { public: A() { printf("A "); } ~A() { printf("deA "); } }; class B { public: B() { printf("B "); } ~B() { printf("deB "); } }; class C : public A, public B { public: C() { printf("C "); } ~C() { printf("deC "); } }; int main() { A* a = new C(); delete a; return 0; }
A. A B C deA
B. C A B deA
C. A B C deC
D. C A B deC
我要构造一个C对象,而C对象是继承自A类和B类(这里要注意先后顺序,写在前面的类先构造),所以构造顺序是A,B,C,最后delete的时候会调用析构函数,而析构函数不是虚函数,所以直接按类型调用也就是说调用的是A类的析构
所以本题选:A
- 在32位环境下,以下程序的输出结构:
#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; }
A. 2000 B. 2004 C. 2014 D. 2024
这段代码糅合了重写和重载,对于整形的foo函数来说,子类重写了父类,构成多态,所以第一个foo函数调用的是子类的,执行
x*20
,而针对字符数组的foo并没有构成重写,所以按类型调用执行sizeof(x)+10
,这里又有一点要注意:字符数组传参的时候其实是传的地址,也就是说char x[14]
其实等价于char*x
,这是32位系统,指针大小是4个字节,所以最后的结果是:2000+14所以本题选:C
- 假设A为抽象类,下列声明()是正确的
A. int fun(A); B. A Obj; C. A fun(int); D. A *p;
抽象类是不能实例化对象的,所以凡是出现和对象有关的,都是错误的;只有D选项,虽然是一个A类的指针,但是并没有创建对象
所以本题选:D
- 下面代码的输出结果:
#include <iostream> #include <vector> using namespace std; int main(void) { vector<int>array; array.push_back(100); array.push_back(300); array.push_back(300); array.push_back(300); array.push_back(300); array.push_back(500); vector<int>::iterator itor; for (itor = array.begin(); itor != array.end(); itor++) { if (*itor == 300) { itor = array.erase(itor); } } for (itor = array.begin(); itor != array.end(); itor++) { cout << *itor << ""; } return 0; }
A. 100 300 300 300 300 500
B. 100 300 300 300 500
C. 100 300 300 500
D. 100 300
vector的删除有迭代器失效的问题,所以在删除的时候都是覆盖删除后再返回当前位置的迭代器,而循环条件中还对迭代器进行了
++
操作,这就导致了会跳过一些数
所以本题选:C