1 程序输出结果为()
#define add(a,b) a+b
int main(){
printf("%d/n", 5*add(3,4));
return 0;
}
A 23
B 35
C 16
D 19
printf("%d/n", 5*add(3,4)); //等价于 printf("%d/n", 5*3+4);
2 有关多态不正确的是()
A C++语法的多态性分编译时的多态和运行时的多态
B 编译时的多态性可通过函数重载实现
C 运行时的多态可通过模板和虚函数实现
D 实现运行时多态性的机制称为动态多态性
C++中的多态性分为两类:
编译时的多态性和运行时的多态性。
编译时的多态性是通过函数重载和模板体现的,其实现机制称为静态绑定;
运行时的多态性是通过虚函数体现的,其实现机制称为动态绑定。
//附加一个个人觉得写的还可以的文章
//https://blog.csdn.net/qq_39412582/article/details/81628254
3 如果一个有序的vector/map/set 中进行数据查找,哪一个查找复杂度更低()
A vector
B map
C set
D 时间复杂度一样
这个目前不太确定,如果有了解具体的
4 关于数据结构一下说法错误的是()
A 红黑树插入操作的平均时间复杂度为O(logn), 最坏为O(logn)
B B+树插入操作的平均时间复杂度为O(logn),最坏为O(logn)
C Hash插入操作的平均时间复杂度为O(logn),最坏为O(n)
D 排序链表插入操作的平均时间复杂度为O(n),最坏为O(n)
Hash表插入操作的平均时间复杂度是O(1),最坏时间复杂度是O(n)
5 若在有序表的关键字序列为(b,c,d,e,f,g,q,r,s,t),则在二分查找b的过程中,先后进行比较的关键字为()
A. f e b
B. f d b
C. g c b
D.g d b
6 在公有派生类的成员函数不能直接访问基类中继承来的某个成员,则该成员一定是基类中的()
A 私有成员
B 公有成员
C 保护成员
D 保护成员或私有成员
7 下列程序输出结果是()
#include
void main(){
int n[][3] = { 10, 20, 30, 40, 50, 60};
int (*p)[3];
p = n;
cout << p[0][0] << “,” << *(p[0] + 1) << ", " << (*p)[2] << endl;
}
A 10,30, 50
B 10,20,30
C 20,40,60
D 10,30,60
8 在IA32架构下,sizeof(Data) 大小为()
union Data{
struct{
int pi;
char key[5];
}
char d[10];
}
A 9
B 10
C 12
D 19
该题充分考察了结构struct和union联合的区别:
联合与结构的本质区别在于内存使用方式的不同。
结构中不同的成员使用不同的存储空间,一个结构所占的内存大小是结构中每个成员所占内存大小的总和,结构体中每个成员相互独立,是不能占用同一存储单元的。
联合大小取决于其中最大的数据类型内存分配大小,联合中内存是叠加存放的。
同一存储区域由不同类型的变量共享,这种数据类型就是联合(也称共同体)。
//附加一个个人觉得还可以的参考文章https://blog.csdn.net/weixin_34127717/article/details/90650066
9 程序段如下:
void GetValue(char* p){
p = (char*)malloc(100);
}
void Test(){
char* str = NULL;
GetValue(str);
strcpy(str, “LongTu”);
strcpy(str + 2, “Game”);
printf(str);
}
运行Test()结果为()
A LongTu Game
B ongTu Game
C LoGame
D 程序崩溃
如果需要改变str,改变变量用一级指针,改变指针用二级指针,但传入的是一级指针,所以没有对str中存放的地址NULL进行改变,所以后面执行strcmp和strcat的内容会让程序崩溃
//https://www.nowcoder.com/questionTerminal/767c46624ac24f9b9d201b2b421d1b75
10 选择合适的容器,需要频繁的进行插入和删除(1),需要在内存快速查找(2),需要在磁盘中快速查找(3)()
A vector/map/list
B list/hash_map/b tree
C deque/tree map/vector
D list/vector/map
大量添加新元素的时候不要使用vector,因为vecter内部的实现方式是利用new2倍的原有内存空间然后再拷贝,影响效率,这个时候可以选择list。
关联容器比如map 查找时候时间复杂度为logn 因为其内部实现方式采用的是红黑树。
二分查找时间复杂度为logn 数组非有序时间复杂度则为n采用遍历方式。
如果频繁出现插入删除操作时尽量避免使用vector和deque
因为其内部是内存连续,插入或者删除都同一块内存其他数据都需要整体的移位。
遍历顺序容器的时候和插入的顺序是一致的,而关联内容则可能会不一定,关联容器可能会内部自动处理变成有序形式。
Vector的数据模型就是数组,这点与C完全兼容、高效随机访问、节省空间。但是缺点是内部插入删除元素代价巨大、动态大小查过自身容量需要申请大量内存做大量拷贝。
List 的数据结构模型是链表,任意位置插入删除元素常量时间复杂度、两个容器融合是常量时间复杂度 On,但是不支持随机访问、比vector占用更多的存储空间。
Deque的数据模型是数组和链表的折衷,高效随机访问、内部插入删除元素效率方便、两端push pop,同样内存占用率比较高。
Map、set、multimap、multiset的数据结构模型是二叉树(红黑树),元素会按照键值排序、查找是对数时间复杂度、通过键值查元素、map提供了下标访问 logn。
如果需要随机访问,用vector,
如果存储元素的数目已知,用vector,
需要任意位置随机插入删除,用list,
只有需要更多在容器的首部尾部插入删除元素,用deque,
元素是复杂结构用list,也可以用vector存储指针(需要额外的精力去维护内存),看需求。
如果操作是基于键值,用set map,
如果需要经常的搜索,用map set,
同样我们如果最小效率速度,可以利用hash_map,相对于以空间换时间,时间复杂度为O1。
二、简答题
1 用位操作把int最高为置零 (5分)
#include
using namespace std;
int main()
{
int a = 32767;
a = a&(~(1<<(32-1)));
cout<<a<<endl;
return 0;
}
2 简述c++对象浅拷贝和深拷贝 (5分)
如果类中没有关于堆的指针时通常用浅拷贝,但是涉及到了堆指针方面的往往要用深拷贝,因为一般浅拷贝是指拷贝后两个对象的指针指向同一内存区域,而深拷贝则是拷贝的对象在堆中自己又创建了一个属于自己的内存,在此基础上又重新编写自己的构造函数。
//大家可以在搜一下相关资料,这里仅我的个人简短理解,未详写,可能日后会详细写= =,若此处有些小问题可能也没太注意,时间太紧了,没有详写,下次一定。
3 实现一个函数删除vector中所有偶数(vector中元素为int) (20分)
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
vector <int> v;
int fun(){
for(std::vector<int>::iterator it = v.begin(); it != v.end(); it++){
if((*it) %2 ==0 ){
it = v.erase(it);
if(it == v.end()) break;
}
}
for(std::vector<int>::iterator it = v.begin(); it != v.end(); it++){
cout<<*it<<endl;
}
}
int main()
{
for(int i=0;i<10;i++){
int num;
cin>>num;
v.push_back(num);
}
fun();
return 0;
}
4 编写类String的构造函数,析构函数,拷贝构造,复制函数 (20分)
已知String的原型为:
class String{
public:
String(const char* str = NULL);
~String();
String(const String& other);
String& operator = (const String& other);
private:
char* m_data;
}
1、构造函数
/*
1、构造函数在构造对象时使用;
2、传入参数的判断;
3、对象的初始化问题。
*/
String::String(const char *str)
{
if ( NULL == str)
{
m_data = new char[1]
*m_data = '\0';
}
else
{
int len = strlen(str);
m_data = new char[len + 1];
strcpy(m_data,str);
}
}
2、析构函数
/*
资源的释放
*/
String::~String(void)
{
delete []m_data;
}
3、拷贝构造函数
/*
1、拷贝构造函数必须在构造对象时使用,即定义对象时;
2、对象初始化问题。
*/
String::String(const String &other)
{
int len = strlen(other.m_data);
m_data = new char[len+1];
strcpy(m_data,other.m_data);
}
4、赋值函数
/*
1、赋值函数使用时,对象肯定已经建立;
2、赋值前,判断是否是自我赋值;
3、赋值前,内存空间的准备:
由于赋值前,对象已占有一定大小内存,但是赋值对象所占内存大小与
对象已占的内存大小不一定一致;
先释放对象已占的内存,然后分配心内存。
4、正常赋值
*/
String & String::operator = (const String &other)
{
if (&other == this)
{
return *this;
}
delete [] m_data;
int len = strlen(other.m_data);
m_data = new char[len+1];
strcpy(m_data,other.m_data);
return *this;
}
补充 1拷贝构造函数与赋值函数的区别?
在看到“=”操作符为对象赋值的时候,
如果是在对象定义时(Test B = (Test)c),此时调用拷贝构造函数;
如果不是在对象定义赋值时(B = c),此时调用赋值函数。
注:构造函数、拷贝构造函数,带有构造两个字,顾名思义,就是在对象声明或定义时才会使用。
2、拷贝构造函数与赋值函数定义的区别?
内存空间角度:
1)拷贝构造函数的使用,是在建立对象时;当时对象没有占有内存,故不需要释放内存,不重新建立内存空间
2)赋值函数的使用,是在对象建立后;当时对象已经占有内存,故需要释放先前内存,然后重新获取内存空间。