一、选择题
1、有一个类A,其数据成员如下,则构造函数中,成员变量一定要通过初始化列表来初始化的是:____。
class A
{
private:
int a;
public:
const int b;
float* &c;
static const char* d;
static double* e;
};
A. a b c
B. b c
C. b c d e
D. b c d
E. b
F. c
答案:B
解析:
const修饰的变量必须被初始化,并且只有一次初始化的机会,因此只能在初始化列表初始化。
引用必须被初始化,并且只有一次初始化的机会,之后不能再引用其他对象了,因此只能在初始化列表中被初始化
static修饰的变量,其内容不能被改变,但是不一定要在初始化列表中初始化
在类A中,a为普通整型,b是const修饰的整型,c是浮点类型指针的引用,d是静态指针常量,e为静态双精度浮点型的指针,因此只有b和c需要在初始化列表中初始化,B正确。
2、C++ 中,有如下类模板定义,已知 b1, b2 是 BigNumber 的两个对象,则下列表达式中错误的是()
template<class T> class BigNumber
{
long n;
public:
BigNumber(T i) :n(i) {}
BigNumber operator+(BigNumber b)
{
return BigNumber(n + b.n);
}
};
A. 3+3
B. b1+3
C. b1+b2
D. 3+b1
答案:D
解析:
这个类重载了+号,其参数是同类对象;
A:两个整型常量相加,表达式正确
B:一个该类对象加上一个整型,该整形会发生隐式类型转换,用这个3构造出一个临时对象,再用这个临时对象拷贝构造出了一个该类对象,因此该表达式也没问题
C:两个该类对象相加,也没问题
D:+的左参数必须为该类对象,而D选项的左参数为整型,故D错误;
3、下列情况中,不会调用拷贝构造函数的是()
A. 用一个对象去初始化同一个类的另一个新对象时
B. 将类的一个对象赋值给该类的另一个对象时
C. 函数的形参对象,调用函数进行形参和实参结合时
D. 函数的返回值是类的对象,函数执行返回调用时
答案:B
解析:
拷贝构造是用一个类去初始化另一个类,赋值重载是将一个类赋值给另一个类
A:用一个类去初始化另一个类的时候调用拷贝构造
B:复制的时候调用的是赋值重载
C:在传参的时候,形参是实参的一份临时拷贝,这个时候调用的是拷贝构造,相当于用实参初始化形参
D:当一个函数返回一个对象的时候,会调用拷贝构造去构造出一份临时拷贝。
5、如果友元函数重载一个运算符时,其参数表中没有任何参数则说明该运算符是()
A. 一元运算符
B. 二元运算符
C. 选项A)和选项B)都可能
D. 重载错误
答案:D
解析: 友元函数的形参中并不会有this指针,因此,若是没有任何参数则重载的运算符也没有任何参数,但是并没有没有参数的运算符,故重载错误,选D
6、在 main 函数中,变量 a 和 b 的构造函数和析构函数的调用顺序是()
class A;
class B;
int main()
{
A a;
B b;
return 0;
}
A. b构造 - a构造 - a析构 - b析构
B. a构造 - a析构 - b构造 - b析构
C. b构造 - a构造 - b析构 - a析构
D. a构造 - b构造 - b析构 - a析构
答案:D
解析:
先定义a对象,再定义b对象,因此是先构造a,再构造b,此时b是在栈顶,释放main函数的栈帧空间时,先释放栈顶元素,因此会先调用b的析构函数,再调用a的析构函数,因此选D。
7、下列静态数据成员的特性中,错误的是()
A. 引用静态数据成员时,要在静态数据成员名前加和作用域符号
B. 说明静态数据成员时前边要加关键字static来修饰
C. 静态数据成员在类体外进行初始化
D. 静态数据成员不是所有对象所共有的
答案:D
解析:
静态成员变量属于这个类的所有对象,并不属于某个具体的对象;选D错误;
8、下面有关c++静态数据成员,说法正确的是()
A. 不能在类内初始化
B. 不能被类的对象调用
C. 不能受private修饰符的作用
D. 可以直接用类名调用
答案:D
解析:
A: 对于普通静态数据成员,确实只能再类内声明,再类外进行定义,而const 修饰的静态整型家族数据成员,可以在类内定义,这是一种特殊语法
B:静态数据成员是属于整个类的,因此可以被类的对象调用
C:静态数据成员可以被private修饰
D:正确
9、在C++中,为了让某个类只能通过new来创建(即如果直接创建对象,编译器将报错),应该()
A. 将构造函数设为私有
B. 将析构函数设为私有
C. 将构造函数和析构函数均设为私有
D. 没有办法能做到
答案:B
解析:
A、C、D: 假设我们将构造函数私有,由于构造函数私有,确实不能直接创建对象了,但是我们也不能通过new构造对象了,因此new主要分为两步,1、申请空间;2、调用构造进行初始化。
B:当我们将析构函数私有后,由于只能在类内调用析构,而我们一般创建的对象在出作用域时调用析构,由于无法调用析构,因此编译器也不允许我们直接构造对象,但由于构造并没有被封,我们可以通过new构造对象,然后如果我们需要析构这个对象,我们需要实现一个公有的方法,在这个公有的方法里调用析构,然后我们通过这个公有的方法进行析构,B正确
10、拷贝构造函数的特点是()
A. 该函数名同类名,也是一种构造函数,该函数返回自身引用
B. 该函数只有一个参数,是对某个对象的引用
C. 每个类都必须有一个拷贝初始化构造函数,如果类中没有说明拷贝构造函数,则编译器系统会自动生成一个缺省拷贝构造函数,作为该类的保护成员
D. 拷贝初始化构造函数的作用是将一个已知对象的数据成员值拷贝给正在创建的另一个同类的对象
答案:D
解析:
A: 构造函数并没有返回值
B:构造函数只有一个参数,该参数必须是同类对象的引用,常常加const修饰。
C:默认生成的构造函数时类的公有成员
D:正确
11、如果MyClass为一个类,执行”MyClass a[5], *b[6]”语言会自动调用该类构造函数的次数是()
A. 2
B. 5
C. 4
D. 9
答案:B
解析:
a对象是一个数组,其中每个元素都是MyClass对象,故调用5次构造,而b是一个数组,其中每个元素类型都是MyClass的指针,不能调用构造,故答案为B;
变形:
若PAT是一个类,则程序运行时,语句“PAT(*ad)[3];”调用PAT的构造函数的次数是()
A. 2
B. 3
C. 0
D. 1
答案:C
解析:
首先我们观察ad是一个指针,其指向的对象是PAT类型的一个数组,数组中有3个元素;但ad仅仅只是一个指针,并不会调用构造函数;故选C
12、在Windows 32位操作系统中,假设字节对齐为4,对于一个空的类A,sizeof(A)的值为()?
A. 0
B. 1
C. 2
D. 4
答案:B
解析: 对于空类来说,其大小为1,这个字节仅仅只是占位作用;故选B。
13、以下程序输出是____。
#include <iostream>
using namespace std;
int main(void)
{
const int a = 10;
int * p = (int *)(&a);
*p = 20;
cout<<"a = "<<a<<", *p = "<<*p<<endl;
return 0;
}
A. 编译阶段报错运行阶段报错
B. a = 10, *p = 10
C. a = 20, *p = 20
D. a = 10, *p = 20
E. a = 20, *p = 10
解析:
在C++中,const修饰的对象其实已经不是普通对象了,而是一个常量,类似于#define定义的常量,在编译阶段,会对常量所在的地方进行替换;
a是一个const修饰的整型常量,p中存放的是a的地址,注意这里&a取出来的数据类型是 const int* ,我们对齐进行强制类型转换成 int*,所以接着我们通过p指针修改a中内容不会报错;当我们打印a时,实际上在编译阶段就进行了替换,因此输出10,而通过p打印其内容时,由于内容被我们改变了,因此打印20;故选D;
14、下面叙述不正确的是()
A. 派生类一般都用公有派生
B. 对基类成员的访问必须是无二义性的
C. 赋值兼容规则也适用于多重继承的组合
D. 父类的公有成员在派生类中仍然是公有的
答案:D
解析:
派生类成员的继承方式取决于继承方式与成员修饰限定符较低的那个,private<protected<public,对于class来说,若不显示定义继承方式则默认为private继承,若用struck定义类,默认继承方式为public继承;故D说法不正确,其他说法均正确;
15、关于虚函数的描述正确的是()
A. 派生类的虚函数与基类的虚函数具有不同的参数个数和类型
B. 内联函数不能是虚函数
C. 派生类必须重新定义基类的虚函数
D. 虚函数可以是一个static型的函数
答案:B
解析:
A: 当派生类的虚函数要被重写时,其虚函数的参数类型、参数个数、函数名、返回值都要相同,协变和析构函数除外。
B:内联函数不可能是虚函数,因此内联函数没有地址,而虚函数的地址要放进虚表中的。
C:派生类只要不重写虚函数就可可以不用重新定义虚函数
D:虚函数不可能为静态成员函数,因此虚函数的调用需要this指针,而静态成员函数没有this指针。
16、当一个类对象的生命周期结束后,关于调用析构函数的描述正确的是()
A. 如果派生类没有定义析构函数,则只调用基类的析构函数
B. 如果基类没有定义析构函数,则只调用派生类的析构函数
C. 先调用派生类的析构函数,后调用基类的析构函数
D. 先调用基类的析构函数,后调用派生类的析构函数
答案:C
解析:
一个类若不显式定义出析构函数,编译器会为这个类生成一个默认的析构函数,而一个拥有继承体系的类,在定义时,会先调用父类构造,然后调用子类构造,而析构的顺序正好相反,先调用子类的析构函数,再调用父类的析构函数;因此答案选C。
17、以下关于纯虚函数的说法,正确的是()
A. 声明纯虚函数的类不能实例化
B. 声明纯虚函数的类成虚基类
C. 子类必须实现基类的虚函数
D. 纯虚函数必须是空函数
答案:A
解析:
A:声明虚函数后这个类是一个抽象类,不能被实例化
B: 虚基类常常出现在菱形继承的情况中,与纯虚函数并无关系。
C:子类如果不重写父类的虚函数,可不用实现父类的虚函数。
D:纯虚函数可以有自己的函数体,只是没有意义。
18、下列描述,正确的一共有多少个()
1)const char *p,这是一个常量指针,p的值不可修改
2)在64位机上,char *p= “abcdefghijk”; sizeof(p)大小为12
3)inline会检查函数参数,所以调用开销显著大于宏
4)重载是编译时确定的,虚函数是运行时绑定的
A. 1
B. 2
C. 3
D. 4
答案:A
解析:
(1)这是一个 常量指针,p的值可以改变,其指向的内容不可修改,错误。
(2)指针在32位机器上大小为固定的4字节,在64位机器下固定大小为8字节,错误。
(3)宏与内联函数都是在调用时展开,效率并无二异,错误
(4)正确
19、C++将父类的析构函数定义为虚函数,下列正确的是哪个()
A. 释放父类指针时能正确释放子类对象
B. 释放子类指针时能正确释放父类对象
C. 这样做是错误的
D. 以上全错
答案:A
解析:
当我们new一个子类对象时,将父类的析构函数设置为虚函数,这样释放父类指针时,满足多态场景。
20、分析一下这段程序的输出
#include<iostream>
using namespace std;
class B
{
public:
B()
{
cout << "default constructor" << " ";
}
~B()
{
cout << "destructed" << " ";
}
B(int i): data(i)
{
cout << "constructed by parameter" << data << " ";
}
private:
int data;
};
B Play( B b)
{
return b;
}
int main(int argc, char *argv[])
{
B temp = Play(5);
return 0;
}
A. constructed by parameter5 destructed destructed
B. constructed by parameter5 destructed
C. default constructor" constructed by parameter5 destructed
D. default constructor" constructed by parameter5 destructed destructed
答案:A
解析:
这题设计编译器的优化,当我们传入5时,编译器直接用5帮我们构造出形参,因此这里调用一次单参构造函数,然后返回时,这里也有一次优化,这里返回时直接把形参b拷贝构造给temp,同时调用析构函数将形参b析构了,然后主函数结束,temp也被析构一次,故选A
21、下列有关this指针使用方法的叙述正确的是()
A. 保证基类保护成员在子类中可以被访问
B. 保证基类私有成员在子类中可以被访问
C. 保证基类共有成员在子类中可以被访问
D. 保证每个对象拥有自己的数据成员,但共享处理这些数据的代码
答案:D
解析: D选项描述正确,概念题。
22、下面说法正确的是()
A. 一个空类默认一定生成构造函数,拷贝构造函数,赋值操作符,引用操作符,析构函数
B. 可以有多个析构函数
C. 析构函数可以为virtual,可以被重载
D. 类的构造函数如果都不是public访问属性,则类的实例无法创建
答案:A
解析:
A:正确
B:析构函数不能重载,只能有一个
C:析构函数可以用virtual修饰,可以在子类中被重写,跟重载并无任何关系。
D:时可以提供一个公有的静态成员函数,在静态成员函数内调用构造函数实例化出对象
二、编程题
1、合法括号序列判断
思路:我们创建一个栈,然后循环遍历这个字符串,判断每一个字符,若为左括号,则进栈,若为右括号,且栈内有元素,则出栈,否则为非法;
class Parenthesis
{
public:
bool chkParenthesis(string A, int n)
{
stack<char> st;
for (int i = 0; i < n; i++) {
if (A[i] == '(') {
st.push('(');
} else if (A[i] == ')') {
// 右括号找不到匹配的左括号了,非法
if (st.empty())
return false;
st.pop();
} else {
return false;
}
}
// 循环结束,若栈为空,则合法,否则为非法
if (st.empty())
return true;
else
return false;
}
};
2、 最近公共祖先
思路:将给出的两个结点大小进行比较,如果不相等,大的那个结点往上找父亲结点;
比如2和6,2和6不相等,6比较大,因此6往上找父亲3,接着继续比较还是不相同,3比较大,继续向上找父亲1,还是继续比较还是不同,2比较大,2往上找父亲1,此时再比较,相同,故1是他们的最近公共祖先;这题比较简单,感兴趣的大家可以去leetcode上找同名题,那题难度比这题就大很多;
class LCA {
public:
int getLCA(int a, int b)
{
while(a != b)
{
// 谁大谁找父亲
if(a > b)
a /= 2;
else
b /= 2;
}
return a;
}
};