1.下面有关C++中为什么用模板类的原因,描述错误的是? ( )
A.可用来创建动态增长和减小的数据结构
B.它是类型无关的,因此具有很高的可复用性
C.它运行时检查数据类型,保证了类型安全
D.它是平台无关的,可移植性
A.模板可以具有非类型参数,用于指定大小,可以根据指定的大小创建动态结构
B.模板最重要的一点就是类型无关,提高了代码复用性
C.模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换,意思就是实例化后的代码在运行时不再进行额外的类型校验,故错误
D.只要支持模板语法,模板的代码就是可移植的
2.下列关于模板的说法正确的是( )
A.模板的实参在任何时候都可以省略
B.类模板与模板类所指的是同一概念
C.类模板的参数必须是虚拟类型的
D.类模板中的成员函数全是函数模板
A. 模板实参省略意思为隐式实例化,一般情况下都使用隐式实例化,不需指定模板类型参数,让编译器进行推导
但有些情况下编译器推导时可能会有歧义,比如:模板参数只有一个类型T,但是用两个不同类型隐式实例化
cpp
template<class T>
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
Add(10, 20); // 正确
Add(1.2, 30); // 编译失败, 改正 Add<int>(1.2, 20) 或者 Add((int)1.2, 20)
return 0;
}
所以模板参数不是任何情况下都可以省略,要结合具体的使用场景,因此A的说法是错误的。
B.类模板是一个类家族,模板类是通过类模板实例化的具体类
C.C++中类模板的参数即为模板参数列表中内容,有两种方式:类型参数和非类型参数
类型参数:即类型参数化,将来实例化为具体的实际类型,有点像函数的形参,形参可以接受不同值的实参
非类型参数:在定义时给定了具体的类型,用该类型定义的为常量,比如:
cpp
template<class T, size_t N>
class array{
// ...
};
D.正确,定义时都必须通过完整的模板语法进行定义。 因为所有类模板的成员函数,放在类外定义时,需要在函数名前加类名,而类名实际为ClassName,所以定义时还需加模板参数列表
cpp
template<class T> size_t Stack<T>::size(){
return _size; }
因此类模板中的成员函数都是函数模板,D正确。
3.在vs2019下 ,下面程序的输出结果正确的是( )
cpp
int main()
{
string str("Hello Bit.");
str.reserve(111);
str.resize(5);
str.reserve(50);
cout<<str.size()<<":"<<str.capacity()<<endl;
return 0;
}
A.10 50
B.5 50
C.5 111
D.10 111
string的
void reserve(size_type n)方法是进行容量调整的当n大于string对象的实际容量时,会将string对象的容量扩大。
但是当n小于string对象的实际容量时,具体是将容量缩小还是容量保持不变,C++标准并没有严格限制,具体版本在实现时,实现者可能会处于效率、内存分配策略等因素,自行决定
在vs2019中,当n小于string对象的实际容量时,容量是不会被缩小的。
代码分析:
str.reserve(111); //调整容量为 111
str.resize(5); //调整元素个数为 5
str.reserve(50); //调整容量为 50,由于调整的容量小于已有空间容量,故容量不会减小
所以size=5 capacity=111
故答案为: C
4.关于代码输出正确的结果是( )(vs2013 环境下编译运行)
cpp
int main(int argc, char *argv[])
{
string a="hello world";
string b=a;
if (a.c_str()==b.c_str())
{
cout<<"true"<<endl;
}
else cout<<"false"<<endl;
string c=b;
c="";
if (a.c_str()==b.c_str())
{
cout<<"true"<<endl;
}
else cout<<"false"<<endl;
a="";
if (a.c_str()==b.c_str())
{
cout<<"true"<<endl;
}
else cout<<"false"<<endl;
return 0;
}
A.false false false
B.true false false
C.true true true
D.true true false
分析:a 和 b的值虽然相同,但是a.c_str()==b.c_str()比较的是存储字符串位置的地址,a和b是两个不同的对象,内部数据存储的位置也不相同,因此不相等,后面c="",a=""与b对象都没有任何的影响,所以都不相等
故答案为:A
5.T是一个数据类型,在vs系列编译器中,debug模式下,关于std::vector::at 和 std::vector::operator\[\] 描述正确的是( )
A.at总是做边界检查, operator\[\] 不做边界检查.
B.at 不做边界检查, operator\[\] 做边界检查.
C.at和operator\[\] 都是会做边界检查的
D.以上都不对
注意题目专门强调了vs系列编译器,debug模式下
at() 和 operator\[\] 都是根据下标获取任意位置元素的,在debug模式下两者都会去做边界检查。
当发生越界行为时,at 是抛异常,operator\[\] 内部的assert会触发
故选择C
6.下面这个代码输出的是( )
cpp
#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 3OO 300 300 500
C.100 300 300 500
D.100 300 500
E.100 500
F.程序错误
程序首先把100 300 300 300 300 500进行尾插
cpp
for(itor=array.begin();itor!=array.end();itor++)
{
if(* itor==300) //向量的数据为300时进行删除
{
//删除之后迭代器进行返回赋值,不会导致迭代器失效,删除当前数据,
//后面的数据相当于会向前移动,此时itor还是指向下一个300数据,
//但是由于循环回去,for循环末尾itor++会让迭代器指向下一个
//数据,因此会错失一次300的比较判断
itor=array.erase(itor);
}
}
所以答案为: 100 300 300 500
7.下面程序的输出结果正确的是( )
cpp
int main()
{
int ar[] = {1,2,3,4,5,6,7,8,9,10};
int n = sizeof(ar) / sizeof(int);
vector<int> v(ar, ar+n);
cout<<v.size()<<":"<<v.capacity()<<endl;
v.reserve(100);
v.resize(20);
cout<<v.size()<<":"<<v.capacity()<<endl;
v.reserve(50);
v.resize(5);
cout<<v.size()<<":"<<v.capacity()<<endl;
}
A.10:10 20:100 5:50
B.10:20 20:100 5:100
C.10:10 20:100 5:100
D.10 10 20:20 20:50
cpp
vector<int> v(ar, ar+n);
cout<<v.size()<<":"<<v.capacity()<<endl; //大小为数组元素个数,因此size=10 capacity=10
v.reserve(100); //预留空间100
v.resize(20); //调整元素为20个,此时元素的size会改变,由于个数小于容量,因此容量不会变小
cout<<v.size()<<":"<<v.capacity()<<endl;// 故size=20 capacity=100
v.reserve(50);//期望预留空间为50,可是现在的空间已经有100个,所以空间不会减小
v.resize(5); //元素个数调整为5
cout<<v.size()<<":"<<v.capacity()<<endl;// 故size=5 capacity=100
所以答案为:C
8.下面有关vector和list的区别,描述错误的是( )
A.vector拥有一段连续的内存空间,因此支持随机存取,如果需要高效的随机读取,应该使用vector
B.list拥有一段不连续的内存空间,如果需要大量的插入和删除,应该使用list
C.vector::iterator支持"+"、"+="、"<"等操作符
D.list::iterator则不支持"+"、"+="、"<"等操作符运算,但是支持了\[\]运算符
A.如果想大量随机读取数据操作,vector是首选的容器
B.如果想大量的插入和删除数据,list效率较高,是首选
C.由于vector底层是连续空间,其迭代器就是相应类型的指针,所以支持对应的操作
D.list迭代器不支持\[\]运算符
9.下面程序的输出结果正确的是( )
c
int main()
{
int array[] = { 1, 2, 3, 4, 0, 5, 6, 7, 8, 9 };
int n = sizeof(array) / sizeof(int);
list<int> mylist(array, array+n);
auto it = mylist.begin();
while (it != mylist.end())
{
if(* it != 0)
cout<<* it<<" ";
else
it = mylist.erase(it);
++it;
}
return 0;
}
程序在使用迭代器取值时,如果不等于0就进行打印,为0时不打印并删除当前节点,所以答案为 B
10.下面有关vector和list的区别,描述正确的是( )
A.两者在尾部插入的效率一样高
B.两者在头部插入的效率一样高
C.两者都提供了push_back和push_front方法
D.两者都提供了迭代器,且迭代器都支持随机访问
A.vector在尾部插入数据不需要移动数据,list为双向循环链表也很容易找到尾部,因此两者在尾部插入数据效率相同
B.vector头部插入效率极其低,需要移动大量数据
C.vector由于在头部插入数据效率很低,所以没有提供push_front方法
D.list不支持随机访问
11.以下程序输出结果为( )
cpp
int main()
{
int ar[] = { 0,1, 2, 3, 4, 5, 6, 7, 8, 9 };
int n = sizeof(ar) / sizeof(int);
list<int> mylist(ar, ar+n);
list<int>::iterator pos = find(mylist.begin(), mylist.end(), 5);
reverse(mylist.begin(), pos);
reverse(pos, mylist.end());
list<int>::const_reverse_iterator crit = mylist.crbegin();
while(crit != mylist.crend())
{
cout<<*crit<<" ";
++crit;
}
cout<<endl;
}
A.4 3 2 1 0 5 6 7 8 9
B.0 1 2 3 4 9 8 7 6 5
C.5 6 7 8 9 0 1 2 3 4
D.5 6 7 8 9 4 3 2 1 0
程序输出结果为 C。过程如下:
初始链表:0 1 2 3 4 5 6 7 8 9
反转前半部分(0~4):4 3 2 1 0 5 6 7 8 9
反转后半部分(5~9):4 3 2 1 0 9 8 7 6 5
反向迭代器输出:从末尾向前,即 5 6 7 8 9 0 1 2 3 4
12.下列代码的运行结果是( )
cpp
void main()
{
stack<char> S;
char x,y;
x='n';y='g';
S.push(x);S.push('i');S.push(y);
S.pop();S.push('r');S.push('t');S.push(x);
S.pop();S.push('s');
while(!S.empty())
{
x = S.top();
S.pop();
cout<<x;
};
cout<<y;
}
A.gstrin
B.string
C.srting
D.stirng
初始:x='n', y='g'。
S.push(x); -> 推入'n',堆栈:底部->顶部:'n'
S.push('i'); -> 推入'i',堆栈:'n','i'
S.push(y); -> 推入'g',堆栈:'n','i','g'
S.pop(); -> 弹出顶部元素,弹出'g',堆栈:'n','i'
S.push('r'); -> 推入'r',堆栈:'n','i','r'
S.push('t'); -> 推入't',堆栈:'n','i','r','t'
S.push(x); -> 推入'n'(x仍是'n'),堆栈:'n','i','r','t','n'
S.pop(); -> 弹出顶部元素,弹出'n',堆栈:'n','i','r','t'
S.push('s'); -> 推入's',堆栈:'n','i','r','t','s'
现在堆栈中有5个元素:从底部到顶部:'n','i','r','t','s'
然后while循环:当堆栈非空时,弹出顶部并输出。所以顺序是:先输出顶部's',然后't',然后'r',然后'i',然后'n'。所以输出:"strin"?注意:顺序:弹出s -> 输出s;弹出t -> 输出t;弹出r -> 输出r;弹出i -> 输出i;弹出n -> 输出n;所以输出"strin"。
然后循环结束后,执行cout<<y; 输出y,y='g'。所以整体输出:"string"?"strin"+"g" = "string"。所以结果是"string"。
13.下列代码的运行结果是( )
cpp
int main()
{
priority_queue<int> a;
priority_queue<int, vector<int>, greater<int> > c;
priority_queue<string> b;
for (int i = 0; i < 5; i++)
{
a.push(i);
c.push(i);
}
while (!a.empty())
{
cout << a.top() << ' ';
a.pop();
}
cout << endl;
while (!c.empty())
{
cout << c.top() << ' ';
c.pop();
}
cout << endl;
b.push("abc");
b.push("abcd");
b.push("cbd");
while (!b.empty())
{
cout << b.top() << ' ';
b.pop();
}
cout << endl;
return 0;
}
A.4 3 2 1 0 0 1 2 3 4 cbd abcd abc
B.0 1 2 3 4 0 1 2 3 4 cbd abcd abc
C.4 3 2 1 0 4 3 2 1 0 abc abcd cbd
D.0 1 2 3 4 4 3 2 1 0 cbd abcd abc
priority_queue a; //a是大堆
priority_queue<int, vector, greater > c; //c指定了比较规则,是小堆
priority_queue b; //b是大堆(注意b是字符串之间的比较)
因此分别建堆的过程是建立a大堆,c小堆,b大堆
所以出队的顺序就按照其优先级大小出队,所以答案为A
14假设cont是一个Container 的示例,里面包含数个元素,那么当CONTAINER为:
1.vector 2.list 3.deque 会导致下面的代码片段崩溃的Container 类型是( )
cpp
int main()
{
Container cont = { 1, 2, 3, 4, 5};
Container::iterator iter, tempIt;
for (iter = cont.begin(); iter != cont.end();)
{
tempIt = iter;
++iter;
cont.erase(tempIt);
}
}
A.1, 2
B.2, 3
C.1, 3
D.1, 2, 3
vector、deque底层都是用了连续空间,所以虽然++iter迭代器了,但是erase(tempit)以后
底层是连续空间,删除会挪动数据,最终导致iter意义变了,已失效了。
而list,不是连续空间,删除以后tempIt虽然失效了,但是不影响iter。
15.说法正确的是( )
A.deque的存储空间为连续空间
B.list迭代器支持随机访问
C.如果需要高效的随机存取,还要大量的首尾的插入删除则建议使用deque
D.vector容量满时,那么插入元素时只需增加当前元素个数的内存即可
A.deque底层总体为不连续空间
B.不支持,因为底层是一个个的不连续节点
C.正确
D.一般会以容量的2倍扩充容量,这是为了减少扩容的次数,减少内存碎片
16.说法正确的是( )
A.deque的存储空间为连续空间
B.list迭代器支持随机访问
C.如果需要高效的随机存取,还要大量的首尾的插入删除则建议使用deque
D.vector容量满时,那么插入元素时只需增加当前元素个数的内存即可
A.deque底层总体为不连续空间
B.不支持,因为底层是一个个的不连续节点
C.正确
D.一般会以容量的2倍扩充容量,这是为了减少扩容的次数,减少内存碎片
17.假设cont是一个Container 的示例,里面包含数个元素,那么当CONTAINER为:
1.vector 2.list 3.deque 会导致下面的代码片段崩溃的Container 类型是( )
cpp
int main()
{
Container cont = { 1, 2, 3, 4, 5};
Container::iterator iter, tempIt;
for (iter = cont.begin(); iter != cont.end();)
{
tempIt = iter;
++iter;
cont.erase(tempIt);
}
}
A.1, 2
B.2, 3
C.1, 3
D.1, 2, 3
此题主要考察cont.erase(tmpit)删除数据之后,迭代器失效相关问题
本题重点要关注的是底层实现
vector、deque底层都是用了连续空间,所以虽然++iter迭代器了,但是erase(tempit)以后
底层是连续空间,删除会挪动数据,最终导致iter意义变了,已失效了。
而list,不是连续空间,删除以后tempIt虽然失效了,但是不影响iter。
18.以下程序运行结果正确的是( )
cpp
template<typename Type>
Type Max(const Type &a, const Type &b)
{
cout<<"This is Max<Type>"<<endl;
return a > b ? a : b;
}
template<>
int Max<int>(const int &a, const int &b)
{
cout<<"This is Max<int>"<<endl;
return a > b ? a : b;
}
template<>
char Max<char>(const char &a, const char &b)
{
cout<<"This is Max<char>"<<endl;
return a > b ? a : b;
}
int Max(const int &a, const int &b)
{
cout<<"This is Max"<<endl;
return a > b ? a : b;
}
int main()
{
Max(10,20);
Max(12.34,23.45);
Max('A','B');
Max<int>(20,30);
return 0;
}
A.This is Max This is Max This is Max This is Max
B.This is Max This is Max This is Max This is Max
C.This is Max This is Max This is Max This is Max
D.This is Max This is Max This is Max This is Max
分析:Max(10,20); //能够直接匹配int参数,调动非模板函数
Max(12.34,23.45); //double类型参数没有最佳匹配函数,此时只能调动模板函数
Max('A','B'); //能够直接匹配char参数,调动非模板函数
Max(20,30); //由于直接实例化了函数,因此要调动模板函数,但是,由于进行函数的int特化,所以会调动特化版本的模板函数
故答案为: A
19.下面关于继承说法不正确的是( )
A.继承可以使用现有类的所有功能,并在无需重新编写原来类的情况下对这些功能进行扩展
B.继承体系中子类必须要体现出与基类的不同
C.子类对象一定比基类对象大
D.继承呈现了面相对象程序设计的层次结构,体现了有简单到复杂的认知过程
A.这是继承的功能,也是代码复用的体现
B.继承除了吸收基类成员之外,一般还需要扩充自己的数据成员,跟基类有所不一样
C.不一定,有可能子类只是改写父类的方法而已,并没有增加其自身的数据成员,则大小一样,故错误
D.继承体现了一定的层次结构和认知过程
20.下面说法正确的是( )
A.派生类构造函数初始化列表的位置必须显式调用基类的构造函数,已完成基类部分成员的初始化
B.派生类构造函数先初始化子类成员,再初始化基类成员
C.派生类析构函数不会自动析构基类部分成员
D.子类构造函数的定义有时需要参考基类构造函数
A.如果父类有默认构造函数,此时就不需要
B.顺序相反,先初始化父类,再是子类
C.会调用,并且按照构造的相反顺序进行调用
D.是的,需要看父类构造函数是否需要参数子类的,从而你决定子类构造函数的定义
