(下午要去面试有道...还是做一点功课比较好...)
1. 多态实现机制
多态性可以简单的概括为“1个接口,多种方法”,在程序运行的过程中才决定调用的机制,通过父类指针调用子类的函数,可以让父类指针有多种形态。
编译器为每个类的对象提供一个虚表指针,这个指针指向对象所属类的虚表(存放了虚函数的地址)。在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向所属类的虚表,从而在调用虚函数时,就能够找到正确的函数。
在构造函数中进行虚表的创建和虚表指针的初始化。在构造子类对象时,要先调用父类的构造函数,此时编译器只“看到了”父类,并不知道后面是否后还有继承者,它初始化父类对象的虚表指针,该虚表指针指向父类的虚表。当执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。
2. 虚函数,纯虚函数
为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。
3. 引用和指针的区别
(1) 引用必须被初始化,指针不必。
(2) 引用初始化以后不能被改变,指针可以改变所指的对象。
(3) 不存在指向空值的引用,但是存在指向空值的指针。
4. 重载和重写的区别
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。(和多态相关)
5. 拷贝构造函数(深拷贝和浅拷贝)
如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数时拷贝构造函数。
浅拷贝是创建了一个对象用一个现成的对象初始化它的时候只是复制了成员(简单赋值)而没有拷贝分配给成员的资源(如给其指针变量成员分配了动态内存); 深拷贝是当一个对象创建时,如果分配了资源,就需要定义自己的拷贝构造函数,使之不但拷贝成员也拷贝分配给它的资源。
6. 内存分配方式及他们的区别
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
7. C++中类型转换机制
(http://blog.csdn.net/kesalin/article/details/8119586)
(1) 隐式类型转换
将某种类型的对象拷贝到另一种不同类型的对象中时就会发生隐式转型,不推荐。
(2) 显示类型转换
cast_name<type>(expression),cast_name可以是以下四种:
Ø static_cast
任何具有明确定义的类型转化,只要不包含底层的const,都可以使用static_cast。可以将非常量转化为常量,反之不可以。不进行安全检查。
Ø const_cast
可以将常量转换为非常量。只能改变常量属性,不能改变表达式的类型。
Ø dynamic_cast
主要用来在继承体系中的安全向下转型,它能安全地将指向基类的指针转型为指向子类的指针或引用,并获知转型动作成功是否。具有类型检查功能,比static_cast更安全,但是存在一定的效率损失。
Ø reinterpret_cast
只用于底层代码,一般我们都用不到它~依赖于机器,为运算对象的位模式提供较低层次上的重新解释。
8. new/delete和malloc/free的区别
new/delete是C++运算符,malloc/free是C语言的标准库函数。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数(除分配/释放内存外,还可能有其他更为详细的工作)。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
9. 进程和线程的区别
1. 字符串里对称子串的最大长度
(腾讯实习生笔试题…当时居然空了……)
输入一个字符串,输出该字符串中对称的子字符串的最大长度。比如输入字符串“goooogle”, 由于该字符串里最长的对称子字符串是“goooog”, 因此输出 6。代码如下:
#include<iostream> #include<string> usingnamespace std; intsymLength(string str); boolisSym(string str); int main(){ string test; int maxSymLength; while(cin>>test){ maxSymLength =symLength(test); cout << maxSymLength<< endl; } return 0; } intsymLength(string str)//字符串中最长的对称子字符串 { int maxSymLength = 0; if(str.size()==1) return 0; for(int i=0; i<str.size()-1; i++) for(int j=str.size()-1;j>i; j--){ string temp =str.substr(i,j-i+1); if(isSym(temp))maxSymLength = max(j-i+1,maxSymLength); } return maxSymLength; } boolisSym(string str)//判断一个字符串是否对称 { for(int i=0;i<str.size()/2;i++){ if(str[i]==str[str.size()-1-i])continue; else return false; } return true; }
自己写的这种方法时间复杂度比较高,参考了网上的代码,可以从o(n^3)降到o(n^2)。
主要思路是如果我们从内向外比较字符,那么对于aba型的字符串,如果我们判断了b是对称的,只需要再左右各移一位就可以判断下一个字符串是否是对称的,这样就能避免重复;原字符串中每一个字符有两种情况,一种是子串是以单个字符为中心对称分布的,即子串的长度是奇数;另一种情况是子串以两个字符串为中心,即子串的长度是偶数。参考代码(http://www.cnblogs.com/python27/archive/2011/12/18/2291977.html):
#include<iostream> #include<string> usingnamespace std; /********************************************************************* * 计算字符串最大对称子串的长度 *********************************************************************/ intMaxSymmetricalSubstringLenth(char* pstring) { int maxlength = 1; char* pchar = pstring + 1; while(*pchar != '\0') { //以该字符串为中心,子串长度为奇数 char *pfirst = pchar - 1; char *psecond = pchar + 1; while(pfirst > pstring &&psecond < &pstring[strlen(pstring) - 1] && *pfirst == *psecond) { pfirst--; psecond++; } int templength = psecond - pfirst + 1; if(templength > maxlength) { maxlength = templength; } //字该字符及之后的字符两个为中心,子串为偶数 pfirst = pchar - 1; psecond = pchar; while(pfirst > pstring &&psecond < &pstring[strlen(pstring) - 1] && *pfirst == *psecond) { pfirst--; psecond++; } templength = psecond - pfirst + 1; if(templength > maxlength) { maxlength = templength; } pchar++; } return maxlength; } int main() { cout<<"Please Enter YourString:"<<endl; char *yourstring = new char[1000]; cin>>yourstring; cout<<"The Max SymmetricalSubString Length is:"<<endl; cout<<MaxSymmetricalSubstringLenth(yourstring)<<endl; delete[] yourstring; return 0; }
2. 打印回形矩阵&蛇形矩阵
(同腾讯实习生笔试题…)
输入一个正整数N,输出N*N的回形/蛇形矩阵。eg.N=4, 如下所示:
回形矩阵:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
蛇形矩阵:
1 2 6 7
3 5 8 13
4 9 12 14
10 11 15 16
代码如下:
#include<iostream> using namespace std; void roundMatrix(int matrix[],int N); void snakeMatrix(int matrix[],int N); void printMatrix(int m[], int M); int main(){ int N; cin >> N; int *round = new int[N*N]; int *snake = new int[N*N]; roundMatrix(round,N); snakeMatrix(snake,N); printMatrix(round,N*N); printMatrix(snake,N*N); delete[] round; delete[] snake; return 0; } void roundMatrix(int matrix[],int N) { int round = 0; int num = 1; while(1) { for(int col=round;col<N-round&&num<=N*N;col++) //从左到右 matrix[round*N+col] = num++; if(num>N*N) break; for(int row=round+1;row<N-round&&num<=N*N;row++) //从上到下 matrix[row*N+N-1-round] = num++; if(num>N*N) break; for(int col=N-2-round;col>=round&&num<=N*N;col--) //从右到左 matrix[(N-1-round)*N+col] = num++; if(num>N*N) break; for(int row=N-2-round;row>round&&num<=N*N;row--) //从下到上 matrix[row*N+round] = num++; if(num>N*N) break; round++; } } void snakeMatrix(int matrix[], int N) { int diag = 0; int num = 1; while(1) { if(diag<N){ //从左下到右上 if(diag%2==0){ for(int col=0;col<=diag&&num<=N*N;col++) matrix[(diag-col)*N+col] = num++; } //从右上到左下 else{ for(int col=diag;col>=0&&num<=N*N;col--) matrix[(diag-col)*N+col] = num++; } } else{ int diag1 = 2*(N-1)-diag; //从左下到右上 if(diag%2==0){ for(int col=N-1-diag1;col<=N-1&&num<=N*N;col++) matrix[(diag-col)*N+col] = num++; } //从右上到左下 else{ for(int col=N-1;col>=N-1-diag1&&num<=N*N;col--) matrix[(diag-col)*N+col] = num++; } } printMatrix(matrix, N*N); if(num>N*N) break; diag++; } } void printMatrix(int m[], int M) { int N = sqrt(M*1.0); for(int i=0;i<M;i++){ if((i+1)%N==0){ cout<<m[i]<<" \n"; } else cout<<m[i]<<" "; } }
3. 二叉树的遍历(非递归)
(基础…)
深度优先搜索算法(前序遍历),可以借助堆栈的数据结构,由于堆栈是后进先出的顺序,由此可以先将右子树压栈,然后再对左子树压栈,这样一来,左子树结点就存在了栈顶上,因此某结点的左子树能在它的右子树遍历之前被遍历。
参考代码:
void depthFirstSearch(Tree* root){ stack<Tree *> nodeStack; //使用C++的STL标准模板库 nodeStack.push(root); Tree *node; while(!nodeStack.empty()){ node = nodeStack.top(); printf(format, node->data); //遍历根结点 nodeStack.pop(); if(node->rchild){ nodeStack.push(node->rchild); //先将右子树压栈 } if(node->lchild){ nodeStack.push(node->lchild); //再将左子树压栈 } } }
广度优先算法,借助队列数据结构,由于队列是先进先出的顺序,因此可以先将左子树入队,然后再将右子树入队。这样一来,左子树结点就存在队头,可以先被访问到。
参考代码:
void breadthFirstSearch(Tree* root){ queue<Tree *> nodeQueue; //使用C++的STL标准模板库 nodeQueue.push(root); Tree *node; while(!nodeQueue.empty()){ node = nodeQueue.front(); nodeQueue.pop(); printf(format, node->data); if(node->lchild){ nodeQueue.push(node->lchild); //先将左子树入队 } if(node->rchild){ nodeQueue.push(node->rchild); //再将右子树入队 } } }
4. 各种排序算法
可参考http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,
冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
可以看一下http://blog.csdn.net/jiuyueguang/article/details/12028393
写下快速排序和归并排序:
(1) 快速排序
通过扫描一次将要排序的数据分割成独立的两部分(通常用首位作为flag),其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
#include<iostream> #include<vector> usingnamespace std; voidquicksort(vector<int> &v, int left, int right); int main(){ vector<int> test; int elem; while(cin>>elem &&elem!='\n'){ test.push_back(elem); } quicksort(test,0,test.size()-1); for(int i=0;i<test.size();i++) cout << test[i]<< " "; return 0; } voidquicksort(vector<int> &v, int left, int right){ if(left<right){ int flag = v[left]; int low = left; int high = right; while(low<high){ while(low<high&& v[high]>flag){ high--; } v[low] = v[high]; while(low < high&& v[low] < flag){ low++; } v[high] = v[low]; } v[low] = flag; quicksort(v,left,low-1); quicksort(v,low+1,right); } }
(2) 归并排序
原理,把原始数组分成若干子数组,对每一个子数组进行排序,继续把子数组与子数组合并,合并后仍然有序,直到全部合并完,形成有序的数组
#include<iostream> #include<vector> usingnamespace std; voidmergesort(int a[], int first, int last, int temp[]); voidmergearray(int a[], int first, int mid, int last, int temp[]); int main(){ int test[6] = {2,3,1,6,8,4}; int after[6]; mergesort(test,0,5,after); for(int i=0;i<6;i++) cout << after[i]<< " "; return 0; } voidmergearray(int a[], int first, int mid, int last, int temp[]) { int i = first, j = mid + 1; int m = mid, n = last; int k = 0; while (i <= m && j <= n) { if (a[i] <= a[j]) temp[k++] = a[i++]; else temp[k++] = a[j++]; } while (i <= m) temp[k++] = a[i++]; while (j <= n) temp[k++] = a[j++]; for (i = 0; i < k; ++i) a[first + i] = temp[i]; } voidmergesort(int a[], int first, int last, int temp[]) { if (first < last) { int mid = (first + last) / 2; mergesort(a, first, mid,temp); //左边有序 mergesort(a, mid + 1, last,temp); //右边有序 mergearray(a, first, mid,last, temp); //再将两个有序数列合并 } }
因为软件园好多地方都在施工,甚至把路都堵住了,顶着大太阳找了好久才找到网易.......对于一个路痴来说简直是.....地图+指南针还经常搞不清往哪里走==心真累。
面试官看起来蛮年轻,典型技术男的长相,先是做了自我介绍,然后针对简历简单聊了几句....然后就拿出了白纸,做了两道编程题:
(1)string型数组,将数组里所有的无序字符串保留下来,如["abc","ab","bca","cba","cd"],则输出为["abc","bca","cba"]。
具体思路就是a.对每个字符串进行排序,生成一个新的有序字符串数组;b.对有序字符串数组扫描一次,用map<string,vector<int>>记录下有序字符串及对应的位置;c.如果记录字符串位置的vector长度大于1,则保留vector中所有位置的无序字符串。
#include<iostream> #include<algorithm> #include<string> #include<vector> #include<map> using namespace std; #define LEN 10 vector<string> findDisorder(string test[], int N); int main(){ string test[LEN] = {"what", "abc", "hawt","ab","bca","acb","www","htaw","mmm"}; vector<string> result; result = findDisorder(test,LEN); for(int i=0;i<result.size();i++) cout << result[i] << " "; return 0; } vector<string> findDisorder(string test[], int N){ vector<string> sorted; vector<string> ans; for(int i=0;i<N;i++){ sorted.push_back(test[i]); sort(sorted[i].begin(),sorted[i].end());//直接使用STL中的排序函数 } map<string,vector<int>> locations;//存放所有的无序字符串及其在源字符串中的位置 for(int i=0;i<sorted.size();i++){ if(locations.count(sorted[i])) locations[sorted[i]].push_back(i); else{ vector<int> loc; loc.push_back(i); locations.insert(pair<string,vector<int>>(sorted[i],loc)); } } map< string,vector<int> >::iterator it; for(it=locations.begin();it!=locations.end();it++){ if(it->second.size()!=1) { for(int i=0;i<it->second.size();i++) ans.push_back(test[it->second[i]]); } } return ans; }
(2)int型数组,将数组里的元素连接拼成最大的数字,如[12,132,89,15]的输出是一个string:“891513212”。
具体思路就是使用归并排序,而比较大小的sort准则需要自己写。简单来说下sort准则:[a,b]可以组合成两个字符串:a+b/b+a,比大小,前者大则a大,后者大则b大。
#include<iostream> #include<string> #include<sstream> using namespace std; #define LEN 3 string maxNum(int test[], int N); void mergesort(string a[], int first, int last, string temp[]); void mergearray(string a[], int first, int mid, int last, string temp[]); bool maxThan(string a, string b); int main(){ int test[LEN] = {98, 12, 120}; string result = maxNum(test,LEN); cout << result << endl; return 0; } string maxNum(int test[], int N) { string *ans = new string[N]; string *temp = new string[N]; string sum=""; for(int i=0;i<N;i++){ stringstream os; os<<test[i]; ans[i] = os.str(); } mergesort(ans,0,N-1,temp); for(int i=0;i<N;i++) sum+=ans[i]; delete[] ans; delete[] temp; return sum; } void mergesort(string a[], int first, int last, string temp[]) { if (first < last) { int mid = (first + last) / 2; mergesort(a, first, mid, temp); //左边有序 mergesort(a, mid + 1, last, temp); //右边有序 mergearray(a, first, mid, last, temp); //再将两个有序数列合并 } } void mergearray(string a[], int first, int mid, int last, string temp[]) { int i = first, j = mid + 1; int m = mid, n = last; int k = 0; while (i<=m && j <= n) { if (maxThan(a[i],a[j])) temp[k++] = a[i++]; else temp[k++] = a[j++]; } while (i <= m) temp[k++] = a[i++]; while (j <= n) temp[k++] = a[j++]; for (i = 0; i < k; ++i) a[first + i] = temp[i]; } bool maxThan(string a, string b){ string str1 = a+b; string str2 = b+a; return str1>str2; }
感觉自己通常可以理清解决问题的思路,但是几乎只能写出伪代码...好多函数记不清...基础还是太差==另外,我之前投的岗位是研发工程实习生,但是听面试官的意思是他们主要做有道系统开发,在这方面完全没有优势....好吧,等待结果....估计要挂...