360面试准备
1)lambda表达式
for_each (array, array + SIZE,
[] (int a){ cout << a << ” “; });
lambda 表达式可以方便地构造匿名函数,如果你的代码里面存在大量的小函数,而这些函数一般只被调用一次,那么不妨将他们重构成 lambda 表达式
作用:使得代码更加简洁
2)自动类型推导和 decltype
auto x=0, 0是int类型,故x也是int类型
auto ci = vi.begin();
decltype用于从对象或表达式中俘获类型,如:
const vector vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;
3)统一的初始化语法
class C
{
int a;
int b;
public:
C(int i, int j);
};
C c {0,0}; //C++11 only. 相当于 C c(0,0);
int* a = new int[3] { 1, 2, 0 }; /C++11 only
class X {
int a[4];
public:
X() : a{1,2,3,4} {} //C++11, 初始化数组成员
};
4)还有一大好事就是对于容器来说,终于可以摆脱 push_back() 调用了,C++11中可以直观地初始化容器了:
// C++11 container initializer
vector vs={ “first”, “second”, “third”};
map singers =
{ {“Lady Gaga”, “+1 (212) 555-7890”},
{“Beyonce Knowles”, “+1 (212) 555-0987”}};
5)C++11 标准的两个新特性:defaulted 和 deleted 函数。
对于 defaulted 函数,编译器会为其自动生成默认的函数定义体,从而获得更高的代码执行效率,
也可免除程序员手动定义该函数的工作量。对于 deleted 函数, 编译器会对其禁用,
从而避免某些非法的函数调用或者类型转换,从而提高代码的安全性。
struct A
{
A()=default; //C++11
virtual ~A()=default; //C++11
};
struct NoCopy
{
NoCopy & operator =( const NoCopy & ) = delete;
NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //编译错误,拷贝构造函数是 deleted 函数
6)nullptr类型
nullptr 是一个新的 C++ 关键字,它是空指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的
void f(int); //#1
void f(char *);//#2
//C++03
f(0); //调用的是哪个 f?
//C++11
f(nullptr) //毫无疑问,调用的是 #2
所有跟指针有关的地方都可以用 nullptr,包括函数指针和成员指针
const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
cout<
例如:
std::unique_ptr p1(new int(5));
std::unique_ptr p2 = p1; // 编译会出错
std::unique_ptr p3 = std::move(p1); // 转移所有权, 现在那块内存归p3所有, p1成为无效的指针.
C++11或boost的shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。
C++11或boost的weak_ptr,弱引用。 引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。
需要手动打破循环引用或使用weak_ptr。顾名思义,weak_ptr是一个弱引用,只引用,不计数。
如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,
内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。
3),智能指针的简单实现。
template <class T>
class myAutoPtr
{
private:
T *myPtr;
public:
explicit myAutoPtr( T *p = 0 )
{
myPtr = p;
}
myAutoPtr &operator=( myAutoPtr &a )
{
if( this == &a )
{
return *this;
}
delete myPtr;
myPtr = a.relase();
return *this;
}
~myAutoPtr()
{
delete myPtr;
}
T& operator*()
{
return *myPtr;
}
T* operator->()
{
return myPtr;
}
T *get()
{
return myPtr;
}
T *relase()
{
T *tmp = myPtr;
myPtr = NULL;
return tmp;
}
void reset( T *p )
{
if( p != myPtr )
{
delete myPtr;
myPtr = p;
}
return;
}
};
5.非递归求树的高度,链表逆序
typedef struct Node
{
int data;
struct Node *left;
struct Node *right;
struct Node()
{
data = 0;
left = right = NULL;
}
}Node, *pNode;
int treeDepth( pNode root )
{
int depth = 0;
if( NULL == root )
{
return depth;
}
queue qu;
qu.push(root);
while( !qu.empty() )
{
int curSize = qu.size();
depth++;
for( int i = 0; i < curSize; i++ )
{
pNode cur = qu.front();
qu.pop();
if( cur->left != NULL )
{
qu.push( cur->left );
}
if( cur->right != NULL )
{
qu.push( cur->right );
}
}
}
return depth;
}
// 逆序一个链表
Node *reverseLink( Node *head )
{
if( NULL == head )
{
return head;
}
Node *cur = NULL;
while( head != NULL )
{
Node *pNext = head->next;
head->next = cur;
cur = head;
head = pNext;
}
return cur;
}
enum Color
{
RED = 0,
BLACK = 1
};
struct redBlackNode
{
int key;
int data;
struct redBlackNode *left;
struct redBlackNode *right;
Color color;
};
2)红黑树性质
节点颜色为红色或黑色,根节点为黑色,叶子节点(Nil,指树尾端NIL指针或NULL结点)为黑色,红节点的子节点为黑色,对于任一节点,其到叶子叶子节点的任一路径含有的黑节点数相同;
3)时间复杂度:能保证在最坏情况下,时间复杂度为O(lgn)
4)红黑树相比于BST和AVL树有什么优点?
红黑树是牺牲了严格的高度平衡的优越条件为代价,它只要求部分地达到平衡要求,降低了对旋转的要求,
从而提高了性能。红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计
,任何不平衡都会在三次旋转之内解决。当然,还有一些更好的,但实现起来更复杂的数据结构能够做到
一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案。
相比于BST,因为红黑树可以能确保树的最长路径不大于两倍的最短路径的长度,所以可以看出它的查找效果
是有最低保证的。在最坏的情况下也可以保证O(logN)的,这是要好于二叉查找树的。因为二叉查找树最坏情
况可以让查找达到O(N)。
红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高,所以在插入和删除中所做的后期维护操作肯定
会比红黑树要耗时好多,但是他们的查找效率都是O(logN),所以红黑树应用还是高于AVL树的. 实际上插入 AVL
树和红黑树的速度取决于你所插入的数据.如果你的数据分布较好,则比较宜于采用 AVL树(例如随机产生系列数),
但是如果你想处理比较杂乱的情况,则红黑树是比较快的
5)红黑树相对于哈希表,在选择使用的时候有什么依据?
权衡三个因素: 查找速度, 数据量, 内存使用,可扩展性。
重于查找速度,内存消耗小:用hash
可扩展性:用红黑树
如何数据基本是静态的,用红黑树,如果数据完全是静态的,做一个哈希表,性能可能会更好一些。
但若你对内存使用特别严格, 希望程序尽可能少消耗内存,那么一定要小心,hash可能会让你陷入尴尬,
特别是当你的hash对象特别多时,你就更无法控制了而且 hash的构造速度较慢。
6).如何扩展红黑树来获得比某个结点小的元素有多少个?
每个节点添加一个size域,表示以该节点x为根的子树的节点数大小
1)找到树中第i小的节点
findIthNode( x, i)
{
r = size[ left[x] ] + 1;
if( i == r )
return x;
else if( i < r )
return findIthNode( left[x], i )
else return findIthNode( right[x], i );
}思路:size[left[x]]表示在对x为根的子树进行中序遍历时排在x之前的个数,递归调用的深度不会超过O(lgn);
2).确定某个结点之前有多少个结点,也就是我们要解决的问题;
OS-Rank( T, x )
{
r = x.left.size + 1;
y = x;
while( y != T.root )
{
if( y == y.p.right )
r = r + y.p.left.size + 1;
y = y.p;
}
return r;
}
思路:x的秩可以视为在对树的中序遍历种,排在x之前的结点个数加上一。最坏情况下,OS-RANK运行时间与树高成正比,所以为O (lgn).
7.扩展数据结构有什么步骤?
1).选择基础数据结构;
2).确定要在基础数据结构种添加哪些信息;
3).验证可用基础数据结构上的基本修改操作来维护这些新添加的信息;
4).设计新的操作。
8.为什么一般hashtable的桶数会取一个素数
设有一个哈希函数
H( c ) = c % N;
当N取一个合数时,最简单的例子是取2^n,比如说取2^3=8,这时候
H( 11100(二进制) ) = H( 28 ) = 4
H( 10100(二进制) ) = H( 20 )= 4
这时候c的二进制第4位(从右向左数)就”失效”了,也就是说,无论第c的4位取什么值,都会导致H( c )的值一样.这时候c的第四位就根本不参与H( c )的运算,这样H( c )就无法完整地反映c的特性,增大了导致冲突的几率.
取其他合数时,都会不同程度的导致c的某些位”失效”,从而在一些常见应用中导致冲突.
但是取质数,基本可以保证c的每一位都参与H( c )的运算,从而在常见应用中减小冲突几率..
8.线程池
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,
任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
2)再哈希法,同时构造多个不同的哈希函数,第一个哈希函数出现冲突,用第二个函数函数;
3)链地址法
4)建立公共溢出区,将哈希表分为基本表和溢出表,凡是和基本表发生冲突的元素,一律填入溢出表;
如何检测:
1)包括手动检测和静态工具分析,代码静态扫描和分析的工具比较多,比如 splint, PC-LINT, BEAM 等
2)动态运行检测,实时检测工具主要有 valgrind, Rational purify 等
特点:热部署(在不停止服务的情况下可以升级ngnix的可执行文件,修改配置文件),可以高并发连接,占用内存少,
处理响应请求很快,具有很高的可靠性。
分布式拒绝服务(DDoS:Distributed Denial of Service)攻击指借助于客户/服务器技术,将多个计算机联合起来作为攻击平台,
对一个或多个目标发动DDoS攻击,从而成倍地提高拒绝服务攻击的威力。
区别:DDOS是DOS攻击中的一种方法,DOS攻击一般是一对一攻击,而DDOS攻击是多台机器同时向一个或多个目标进行攻击,
就是控制多台电脑对同一目标进行DOS攻击。
基本操作
实现DMA传送的基本操作如下:
1、外设可通过DMA控制器向CPU发出DMA请求;
2、CPU响应DMA请求,系统转变为DMA工作方式,并把总线控制权交给DMA控制器;
3、由DMA控制器发送存储器地址,并决定传送数据块的长度;
4、执行DMA传送;
5、DMA操作结束,并把总线控制权交还CPU。
15.Unix/Windows中进程通信的几种方式
现在linux使用的进程间通信方式:
(1)管道(pipe)和有名管道(FIFO)
(2)信号(signal)
(3)消息队列
(4)共享内存
(5)信号量
(6)套接字(socket)
Windows中进程通信的几种方式
1)剪贴板
2)文件映射
3)使用共享内存方式
4)windows套接字
5)管道
6)邮件槽
临界区与信号量的区别
保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。
信号允许多个线程同时使用共享资源 ,这与操作系统中的PV操作相同。它指出了同时访问共享 资源的线程 最大数目。
它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
BST树转换为双向链表
单例模式实现
class CSingleInstance
{
private:
CSingleInstance(){}
CSingleInstance( const CSingleInstance & ){}
CSingleInstance& operator=( const CSingleInstance & ){}
static CSingleInstance *pInstance;
public:
static CSingleInstance *getInstance()
{
return pInstance;
}
};
CSingleInstance * CSingleInstance::pInstance = new CSingleInstance();
string类简单实现
assert(index>=0 && index<=strLength);
free和delete如何知道应该释放多少内存
分配内存的算法有关,依赖于实现,在分配每块内存的时候,都有额外的空间来记录分配内存的大小的
用前面四个字节来保存大小
分配时会簿记,删除时先查询是否存在
一般的做法是在分配的内存前边加一个长度值,假如你申请4字节的内存,那系统很可能是分配了8字节,然后4字节记录长度,
另4字节给你用。但这个是compiler specific的,也不排除有编译器会使用查表法或其它更高明的办法。
21.new和malloc的区别
运算符和函数的区别
返回值不同,一个需要强制转换,一个不需要
是否调用构造函数
分配失败一个抛出异常,一个不抛出而返回NULL
new自动计算需要分配的空间,而malloc需要手工计算字节数
new是类型安全的,而malloc不是,比如:
int* p = new float[2]; // 编译时指出错误
int* p = malloc(2*sizeof(float)); // 编译时无法指出错误
既然new/delete的功能完全覆盖了malloc/free,为什么C++还保留malloc/free呢?因为C++程序经常要调用C函数,
而C程序只能用malloc/free管理动态内存。
23.写个类A,声明类A指针指向NULL,调用类A的方法会有什么后果,编译通过吗?
可以编译通过,但是运行时,如果调用的是普通函数,如果普通函数中访问了非静态成员变量,则运行时会出现
错误,没有访问成员变量或访问的是静态成员变量的话,正常调用而没有错误,但是如果调用的是是virtual函数,
则会出现运行时错误。
原因:普通函数放在全局内存区中,可以访问,虚函数是通过虚函数表来调用的,首先要查找到虚函数表,而
指向虚函数表的指针存放在对象中,又此时没有对象生成,故出现错误;
25.vim的替换语句,abc换成def
语法为 :[addr]s/源字符串/目的字符串/[option]
全局替换命令为::%s/源字符串/目的字符串/g
#include
using namespace std;
void func( int m )
{
cout << m << endl;
return;
}
int main()
{
int ret = 0;
int n = 2^31;
cout << sizeof(n) << endl; // 4
long long n1 = 2^31;
cout << sizeof(n1) << endl; // 8
cout << n1 << endl; // 29
cout << n << endl; // 29
func( 2^31 - 3 ); // 30
return ret;
}
==================================================================================
360 企业安全集团,服务端开发工程师-C++——珠海
本来视频面试,后来他说他那边有问题,就电话面试
1. 自我介绍
2. 问了下hadoop, 研究生有学过没,storm和spark的区别
3. 讲下memcached, memcached集群中有一个节点失效了,怎么处理,哈希一致性算法实现;
4. 看了libevent,讲下同步,异步,阻塞,非阻塞的区别
5. 有了解服务端开发的相关技术吗,讲下;
6. 有github账号吗,有看源代码吗
7. 问了我喜欢做服务端的哪方面;
8. 有用过哪些数据库没,我说研究生期间没有
9. 最后他说没有什么问题 ,就结束了,全程14分钟左右。