【笔试】游戏服务器开发笔试记录

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

该题充分考察了结构structunion联合的区别:
联合与结构的本质区别在于内存使用方式的不同。
结构中不同的成员使用不同的存储空间,一个结构所占的内存大小是结构中每个成员所占内存大小的总和,结构体中每个成员相互独立,是不能占用同一存储单元的。
联合大小取决于其中最大的数据类型内存分配大小,联合中内存是叠加存放的。
同一存储区域由不同类型的变量共享,这种数据类型就是联合(也称共同体)//附加一个个人觉得还可以的参考文章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)赋值函数的使用,是在对象建立后;当时对象已经占有内存,故需要释放先前内存,然后重新获取内存空间。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值