zoukankan      html  css  js  c++  java
  • 浅析C++基础知识

       近期想对C++的面试题目进行一下更加详细的整理。事实上认真思考一下C++程序猿的面试,我们能够发现对程序猿的能力的考察总是万变不离当中,这些基础知识主要分为五部分:一、 C/C++基础知识 二、 C/C++的一些特性,如面向对象,内存管理  三、 基础的数据结构编程的知识。 四、stl的一些基础知识。五、网络编程、多线程的知识、异常处理基础知识

        本文试图覆盖C/C++面试的每一个知识点,所以对每一个知识点介绍的并不深入。本文适合已经对一下详细知识有所了解的人,我对每一个点都有粗略的解说,假设想深入理解请阅读相关详细知识。

        一、 C/C++的基础知识:包含指针、字符串处理、内存管理。

        二、面向对象基础知识:包含多态、继承、封装。 多态的实现方式?多态的优点?

        三、基础的数据结构面试:数组、链表、字符串操作、二叉树。这里要说的话就说的非常多了,详细的有使用一些数据结构包含stl list, vector, map, hashmap, set。

        四、stl的一些基础知识。

        五、网络编程、多线程和异常处理的基础知识。


    一、C/C++基础知识:

                 1. C/C++内存管理:C/C++的内存分为:堆、栈、自由数据区、静态数据区和常量数据区。

                         栈: 函数运行时,函数内部的局部变量在栈上创建,函数运行结束栈的存储空间被自己主动释放。

                         堆:就是那些new分配的内存块,须要程序猿调用delete释放掉。

                         自由数据区:就是那些调用malloc的内存块,须要程序猿调用free释放掉。

                         全局存储区:全局变量和静态变量被存放到同一块存储区域。

                         静态存储区:这里存放常量,不同意改动。

                         堆和栈的差别:a. 管理方式不同:堆须要程序猿手动维护。 b. 栈的存储空间远小于堆。堆差点儿不受限制。 c. 生长方式不同。栈是从高地址空间向低地址空间生长,堆是从低地址空间向高地址空间生长。 d. 效率不同:堆要远快于栈。

                         new和delete的差别:new和delete是C++的函数会调用,构造函数和析构函数。而malloc和delete仅仅是分配相应的存储空间。

                         注意问题: 1.  意外处理:内存申请失败处理。2. 内存泄漏:申请的地址空间一定要释放。 3. 野指针:避免指针未赋值使用,一定要初始化。

                         给出一个图吧,关于内存空间的分配的:

    左边的是UNIX/LINUX系统的运行文件,右边是相应进程逻辑地址空间的划分情况。     

              2.  小端存储和大端存储:

                         小段:高位存在高地址上;大端:低位存在高地址上。

                         判定代码:

                                           unsigned short test = 0x1234;

                                     if( *(unsigned char *)&test=0x12 )//

                                            return Big_Edian;//高位在低地址空间 大端存储

              3.  C++的指针使用:    

                         a. 指针数组 与 数组指针: 注意[]的结合优先级比*要高。那么int *p[3];我们就得到了一个数组,数组的每一个元素是int *的。而int (*p)[3];//我们就得到了一个指针,指向的是一个长度为3的一维数组。

                         b. 函数指针:比如对函数:int funcA(int a,int b);  我们能够定义一个指针指向这个函数:int (*func)(int,int);  func=funcA;

                         c. ptr++; 指针ptr的值加上了sizeof(ptr); 

               4. 运算符优先级:

                         a. 比較常考的就是 ++和--与*、&、.的优先级对照:注意正确的优先级是:.  >  ++  >  --  >  *  >  &

               5. 二维数组的动态申请:

                         int **a=new int *[10];

                         for(i=0;i<10;i++)

                               a[i]=new int[10];

               6.  extern "C"声明的作用:

                         C++中引用C语言的函数和变量须要使用extern "C"来进行修饰。由于C++为了支持多态,函数和变量命名的方式和C不同,因此假设一个模块的函数和变量须要被其它模块使用,就须要使用extern "C"来修饰。该修饰制定编译器变量和函数是依照C语言方式进行链接的。

              7. 指针和引用的差别,指针和数组的差别,const与define的差别:

                         指针和引用的差别:引用能够看作是变量的别名,不能够指向其它的地址,不能为空。

                         指针和数组的差别:指针能够指向其它地址,使用sizeof计算时两者的结果不同。

                         const与指针的差别:const定义的变量有类型,而define仅仅是编译器的简单替换。

               8. 怎样判定程序是由C/C++编译器编译出来的?

                         #ifdef __cplusplus

                         #else

                         #endif

               9. const的作用,static的作用。

                         a. const 指定变量不可改动。

                         b. static 指定变量存在静态数据区,而且仅仅初始化一次。

               10. 变量字节对齐:为了加快内存寻址的效率,在C++内存管理时一般选取的是4字节对齐。例如以下变量占用8个字节:

                         struct Node{

                               int a,

                               char b

                         };

               11. const与指针:

                         char * const p; //指针不可改

               char const *p; //指针指向的对象不可改动

         12. 操作符重载:

               C/C++ 里大多数运算符都能够在 C++ 中被重载。C 的运算符中仅仅有 . 和 ?:(以及sizeof,技术上能够看作一个运算符)不能够被重载。C++ 添加了一些自己的运算符,除了 :: 和 .* 外,大多数都能够被重载。

               a. 单目操作符:

                   Point &Point:: operator++();//++a;

                   Point &Point:: operator++(int);//a++

               b. 双目操作符:

                   Point &Point:: operator+=(Point b)// +=

               源码例如以下:

    class Point{
    public:
        int x;
        int y;
    public:
        void Print(){
            cout<<"x="<<this->x<<endl;
            cout<<"y="<<this->y<<endl;
        }
        Point(){
            x=0;
            y=0;
        }
        Point(const Point &a){
            x=a.x;
            y=a.y;
        }
        Point(int x,int y){
            this->x=x;
            this->y=y;
        }
        Point& operator+=(Point b);
        Point& operator++();
        Point& operator++(int);
    };
    Point& Point::operator+=(Point b){
        x += b.x;
        y += b.y;
        return *this;
    }
    
    Point& Point::operator++(){
        this->x++;
        this->y++;
        return *this;
    }
    Point& Point::operator++(int){
        Point a(*this);
        this->x++;
        this->y++;
        return a;
    }
    int main(){
        Point *a=new Point(1,2);
        Point *b=new Point(3,4);
        Point c(10,20);
        *a+=c;
        a->Print();
        ++c;
        c.Print();
        c++;
        c.Print();
        return 0;
    }
    

              13. 函数调用传值与传引用

                         传值不会改动传入參数的值,而传引用会改动传入參数的值。

              14. volatile与C/C++的四种cast函数:

                         volatile修饰的变量,编译器不会做优化,每次都是到内存中去读取或者写入改动。C++有四种cast函数:static_cast,dynamic_cast,const_cast,volatile_cast。

              15. C++ 类的实例化假设没有參数是不须要括号的,否者就是调用函数而且返回相应的对象,比如例如以下代码就会报错:

    struct Test
    {
    	Test(int ) { }
    	Test() { }
    	void fun() { }
    };
    
    int main(void)
    {
    	Test a(1);
    	a.fun();
    	Test b;
    	b.fun();
    	return 0;
    }

              15. C++ 里面的const修饰函数时,标志该函数不会改动this指针的内容。并且const不能修饰非类内部的函数。

              16. const能够重载,这是基于编译器编译时对函数进行重命名实现的。比如f( int a), f(const int a)是两个不同的函数了。

              17. override(覆盖), overload(重载), polymorphism(多态)

              18. const char* a, char const*, char*const的差别: const char * pa;//一个指向const类型的指针,char * const pc;//const修饰的指针

              19. typename和class的差别:在模版中使用时,class与typename能够替换。在嵌套依赖类型中,能够声明typename后面的字符串为类型名,而不是变量。比如:

    class MyArray //临时还不是非常懂,就先睡觉了。
    { 
    public:
    typedef int LengthType;
    .....
    }
    
    template<class T>
    void MyMethod( T myarr ) 
    { 
    typedef typename T::LengthType LengthType; 
    LengthType length = myarr.GetLength; 
    }


    二、面向对象和C++特性描写叙述:

                 1. 面向对象三个基本特征:继承、多态和封装。

              2. 多态:多态是面向对象继承和封装的第三个重要特征,它在执行时通过派生类和虚函数来实现,基类和派生类使用相同的函数名,完毕不同的操作和实现相隔离的还有一类接口。多态提高了代码的隐藏细节实现了代码复用。

              3. 什么是纯虚函数和纯虚类:有些情况下基类不能对虚函数有效的实现,我们能够把它声明为纯虚函数。此时基类也无法生成对象。

              4. 什么是虚函数:虚函数是类内部使用virtual修饰的,訪问权限是public的,而且非静态成员函数。虚函数是为了实现动态连接,定义了虚函数以后对基类和派生类的虚函数以相同形式定义,当程序发现virtual虚函数以后会自己主动的将其作为动态链接来处理。纯虚函数在继承类实现时,须要所有实现。

                         如public virtual function f()=0;

                注:下列函数不能被声明为虚函数:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。

              5. 执行时绑定和虚函数表:

                         动态绑定:绑定的对象是动态类型,其特性依赖于对象的动态特性。

                         虚函数表: C++的多态是通过动态绑定和虚函数表来实现的。这是虚函数的地址表,存储着函数在内存的地址。一般类的对象实例地址就是虚函数表。有虚函数覆盖时,虚函数的地址存储的是被覆盖的子类的函数的地址。

                        事实上知道虚函数表了,我们就能够通过操作指针的方式訪问一些额外的数据(当然假设直接写成代码会编译不通过的)。对于基类指针指向派生类时:我们能够通过内存訪问派生类未覆盖基类的函数,訪问基类的非public函数。

              6. 空类的大小不是零:由于为了确保两个实例在内存中的地址空间不同。

                            例如以下,A的大小为1,B由于有虚函数表的大小为4.

                                  class A{};

                                  class B:public virtual A{};

                           多重继承的大小也是1,例如以下:                              

                                 class Father1{}; class Father2{};

                                 class Child:Father1, Father2{};

              7. 深拷贝与浅拷贝:

                         C++会有一个默认的拷贝构造函数,将某类的变量所有赋值到新的类中。但是这样的情况有两个例外:(1). 类的默认构造函数做了一些额外的事情,比方使用静态数据统计类被初始化的次数(此时浅拷贝也无法解决这个问题,须要额外的改动拷贝构造函数)。 (2). 类内部有动态成员:此时发生拷贝时就须要对类内部的动态成员又一次申请内存空间,然后将动态变量的值拷贝过来。

               8. 仅仅要在基类中使用了virtualkeyword,那么派生类中不管是否加入virtual,都能够实现多态。

               9. 虚拟析构函数:在使用基类指针指向派生类时,假设未定义虚拟析构函数那么派生类的析构函数不会被调用,动态数据也不会被释放。

                  构造函数和析构函数的调用顺序为:先基类构造函数,再派生类构造函数。析构时,先派生类析构函数,再基类析构函数。

               10. public,protected, private权限:privated:类成员函数、友元函数。 protected:派生类、类成员函数、友元函数,public全部

               11.  可重入函数(多线程安全),即该函数能够被中断,就意味着它除了自己栈上的变量以外不依赖不论什么环境。

               12.  操作系统内存管理有堆式管理、页式管理、段式管理和段页式管理。堆式管理把内存分为一大块一大块的,所需程序片段不在主村时就分配一块主存程序,把程序片段load入主存。页式管理:把主页分为一页一页的,每一页的空间都比块小非常多。段式管理:把主存分为一段一段的,每一段的空间比一页一页的空间小非常多。段页式管理。

    三、基础的数据结构编程知识:

              事实上我理解作为面试来说,通常会面时应聘者的基础知识,假设对于过于复杂的图程序,动态规划一般不太适合。而对于数组、链表、二叉树、字符串处理的考察,既能考察出应聘者的基础知识、代码风格、命名书写规范,又能考察出应聘者的代码是否包括bug。而比較深入的一些题目一般仅仅作为脑筋急转弯或者给出思路,博主在此提醒各位,面试时准确性更加重要,假设一时想不出来,能够首先给出一个效率较低的实现,然后再一步步优化。以下分析一下对于数组、链表、二叉树和字符串处理的常考题目:
              1. 数组:一般到了数组就会考察排序(稳定性、平均、最好、最差时间复杂度/内存使用、不同情况下那种最快)、二分的特性。
                     2. 链表:链表的插入,逆序,推断链表相交,找到链表交点,链表的删除。
              3. 二叉树:二叉树的深度、前序/中序/后序遍历、二叉树的叶子数目、二叉树的节点数目、二叉树的层次遍历。
              4. 字符串的处理:atoi,itoa,字符串替换、字符串的查找。再次特意提醒一下,字符串处理设计指针的处理所以指针的内存訪问控制,须要使用很多其它的注意,比方參数为空,内存申请失败,返回值控制,指针加减。
       
              1. 数组:
                     1).  排序:各种排序算法的对照方下图所看到的
    排序法 平均时间 最差情形 稳定度 额外空间 备注
    冒泡 O(n2)     O(n2) 稳定 O(1) n小时较好
    交换     O(n2)     O(n2) 不稳定 O(1) n小时较好
    选择 O(n2) O(n2) 不稳定 O(1) n小时较好
    插入 O(n2) O(n2) 稳定 O(1) 大部分已排序时较好
    基数 O(logRB) O(logRB) 稳定 O(n)

    B是真数(0-9),

    R是基数(个十百)

    Shell O(nlogn) O(ns) 1<s<2 不稳定 O(1) s是所选分组
    高速 O(nlogn) O(n2) 不稳定 O(nlogn) n大时较好
    归并 O(nlogn) O(nlogn) 稳定 O(1) n大时较好
    O(nlogn) O(nlogn) 不稳定 O(1) n大时较好
    
                        2) 详细的每种排序算法的实现:
                         a) 高速排序实现:
    int partition(int a[],int low,int high){
        int data=a[low],tem;
        int i=low+1,j=low+1;
    
        while(j<=high){
            if(a[j]>data)
                j++;
            else{
                tem=a[i];
                a[i]=a[j];
                a[j]=tem;
                i++;
                j++;
            }
        }
        a[low]=a[i-1];
        a[i-1]=data;
        return i-1;
    
    }
    
    
    void qsort1(int a[],int low,int high){
        if(low>=high)
            return;
        int mid=partition(a,low,high);
        if(mid-1>low)
            qsort1(a,low,mid-1);
        if(high>mid+1)
            qsort1(a,mid+1,high);
    
    }
                         b) 堆排序实现:
    void HeapModify(int a[], int i, int length){
        int j=-1;
        if( i*2+1 < length ){
            if( a[i*2+1]>a[i]){
                j=i*2+1;
            }
        }
        if( i*2 +2 < length){
            if( a[i*2 +2] > a[i] && a[2*i+2] > a[2*i+1]){
                    j=i*2+2;
            }
        }
    
        if( j> 0){
            int tem;
            tem=a[i];
            a[i]=a[j];
            a[j]=tem;
            HeapModify(a,j,length);
        }
    }
    void Heap_sort(int a[],int length){
        int i,tem;
        for(i=length/2;i>=0;i--){
            HeapModify(a,i,length);
        }
    
        for(i=0;i<length-1;i++){
            tem=a[0];
            a[0]=a[length-1-i];
            a[length-1-i]=tem;
            HeapModify(a,0,length-i-1);
        }
    }
                        
                         c) 最大子数组求和:
    int Max_Sum(const int a[],const int length_a){
        int length,i,max1=0;
        int *b=new int[10];
    
        max1=b[0]=a[0];
        for(i=1;i<length_a;i++){
            b[i]=max(b[i-1]+a[i],a[i]);
            if(b[i]>max1){
                max1=b[i];
            }
        }
        return max1;
    }

    3) 字符串处理程序:
                          a). atoi实现:
    int atoi1(char *str){
        assert(str!=NULL);
        int result=0,pos=1;
        if(*str=='-'){
            pos=-1;
            str++;
        }
        while(*str!=''){
            assert(*str<='9'&&*str>='0');
            result=result*10+*str-'0';
            str++;
        }
        return result*pos;
    
    }
                          b). itoa实现:
    char *itoa1(int num,int k){
        char data[]="0123456789ABCDEF";
        bool pos=true;
        int count=1,tem;
        int num1=num;
        if(num<0){
            num=-num;
            pos=false;
            count++;
        }
        while(num>0){
            num=num/k;
            count=count+1;
        }
        char *result=new char[count];
        char *presult=result,*pstr;
        if(pos==false){
            *presult++='-';
        }
        pstr=presult;
    
        while(num1>0){
            *presult++=data[num1%k];
            num1=num1/k;
        }
        *presult--='';
        while(pstr<presult){
            tem=*pstr;
            *pstr=*presult;
            *presult=tem;
            *presult--;
            *pstr++;
        }
    
        return result;
    }
                          c). 将字符串空格替换为目的串的实现:
    char * Replace_Str(char * str, char * target){
        assert(str!=NULL&&target!=NULL);
        char *pstr=str;
        int count=0;
        while(*pstr!=''){
            if(*pstr==' '){
                count++;
            }
            pstr++;
        }
        char * result=new char[sizeof(str)+count*strlen(target)];
        char *presult=result;
        for(pstr=str;pstr!='';pstr++){
            if(*pstr!=' '){
                *presult=*pstr;
            }
            else{
                char *ptarget=target;
                while(*ptarget!=''){
                    *presult++=*ptarget++;
                }
            }
        }
        *presult='';
        return result;
    }
                         d). strcpy实现:
    void strcpy1(char * source, char * dest){
        assert(source!=NULL&&dest!=NULL);
        char * psource=source, *pdest=dest;
        while(*psource!=''){
            *pdest++=*psource++;
        }
        *pdest='';
    }
                         e). 查找目的串第一次出现位置(最快的肯定是KMP O(M+N)),或者查找目的串在源串中的出现次数:
    <span style="font-size:14px;">int Find_Str(const char* source,const char* target){ //查找目的串在源串中出现的位置,最弱的方法
        assert(source!=NULL&&target!=NULL);
    
        int i,j,i1;
        int len_source=strlen(source),len_target=strlen(target);
    
        for(i=0;i<len_source;i++){
            i1=i;
            for(j=0;source[i1]==target[j]&&j<len_target;i1++,j++);
            if(j==len_target)
                return i;
        }
        return -1;
    }</span>
                         f). memcopy实现:
    void *memcpy1(void * dest, const void * source, size_t num){//注意须要考虑内存重叠的部分,特别的dest在source数据区间内部时候的处理
        assert(dest!=NULL&&source!=NULL);
        int i;
        byte *psource=(byte *) source;
        byte *pdest=(byte *) dest;
    
        if(pdest>psource&&pdest<psource+num){
            for(i=num-1;i>=0;i--)
                *(pdest+i)=*(psource+i);
        }else{
            for(i=0;i<=num;i++)
                *(pdest+i)=*(psource+i);
        }
    
        return dest;
    }


                        3) 链表处理:
                         a). 链表相交判定:
                         b). 链表逆序:
                         c). 两个有序链表拼接成为一个有序链表:
                4) 二叉树处理:
                         a). 二叉树的前序/后序/中序遍历:
                                   a. 二叉树前序/非递归:
    void Pre_Traverse_Recur(NODE *root){//前序递归
        if(root==NULL)
            return;
        cout<<root->data;
        Pre_Traverse_Recur(root->left);
        Pre_Traverse_Recur(root->right);
    }
    void Pre_Traverse(NODE *root){<span style="font-family: Arial, Helvetica, sans-serif;">//前序非递归</span>
        stack<NODE*> stack1;
        NODE *p=root;
    
        while(p!=NULL||!stack1.empty()){
            while(p!=NULL){
                cout<<p->data;
                stack1.push(p);
                p=p->left;
            }
            if(!stack1.empty()){
                p=stack1.top();
                stack1.pop();
                p=p->right;
            }
        }
    }
                                   b. 二叉树中序/非递归:
    void Inorder_Traverse_Recur(NODE *root){//中序递归
        if(root==NULL)
            return;
        Pre_Traverse_Recur(root->left);
        cout<<root->data;
        Pre_Traverse_Recur(root->right);
    }
    void Inorder_Traverse(NODE *root){<span style="font-family: Arial, Helvetica, sans-serif;">//中序非递归</span>
        stack <NODE*> stack1;
        NODE *p=root;
        while(p!=NULL||!stack1.empty()){
            while(p!=NULL){
                stack1.push(p);
                p=p->left;
            }
            if(!stack1.empty()){
                p=stack1.top();
                cout<<p->data;
                stack1.pop();
                p=p->right;
            }
        }
    }
                                   c. 二叉树后序/非递归:
    void Postorder_Traverse_Recur(NODE *root){//后序递归
        if(root==NULL)
            return;
        Postorder_Traverse_Recur(root->left);
        Postorder_Traverse_Recur(root->right);
        cout<<root->data;
    }
    
    #include <hash_map>
    using namespace std;
    using namespace __gnu_cxx;
    void Postorder_Traverse(NODE *root){//非递归后序遍历
        stack <NODE *> stack1;
        hash_map <int,int> hashmap1;
    
    
        assert(root!=NULL);
        NODE *p=root;
    
    
        while(p!=NULL){
            stack1.push(p);
            p=p->left;
            hashmap1[stack1.size()]=0;
        }
    
    
        while(!stack1.empty()){
            p=stack1.top();
    
    
            while(p->right&&hashmap1[stack1.size()]==0){
                hashmap1[stack1.size()]=1;
                p=p->right;
                while(p!=NULL){
                    stack1.push(p);
                    hashmap1[stack1.size()]=0;
                    p=p->left;
                }
                p=stack1.top();
            }
            p=stack1.top();
            cout<<p->data;
            stack1.pop();
        }
    }
    
                         b). 二叉树的深度/节点数目/叶子数目:
                         c). 二叉树的两个节点的最短距离,最低公共祖先:
                         d). 全然二叉树判定:
                         e). 二叉树镜像:
                5) 其它:
                         a). B/B+树:
    四、STL基础知识:
               1. STL:标准模板库,它由算法、迭代器、容器组成。
               2. STL容器简单介绍,STL容器大致能够分为三类:
                       1). 序列容器:vector, list, deque, string
                       2). 关联容器:map, set, hashmap, multiset, hash_set
                       3). 其它杂项:stack, queue, valarray, bitset  
               3. list使用:
        list<int> lst1;
        list<int>::iterator itor;
        lst1.push_front(1);
        lst1.push_back(2);
        int i=lst1.front();
        list1.size();
        list1.empty();
               4. stack使用:
        stack<int> stack1;
        stack1.push(10);
        int i=stack1.top();
        stack1.pop();
        stack1.size();
        stack1.empty();
               5. vector使用:
        vector<int> v;
        vector<int> ::iterator itor;
        for(int i=0;i<10;i++){
            v.push_back(i);
        }
        itor=v.begin();
        v.insert(itor,55);
        v.erase(itor);
    
        for(itor=v.begin();itor!=v.end();itor++){
            cout<<*itor<<endl;
        }
       vector<int>::iterator findit = find(v.begin(),v.end(),123);
        if(findit==v.end())
            cout<<"Not exist!!"<<endl;
               6. Map/HashMap使用:
        map <int,int> map1;
        map1[5]=5;
        map1.insert(pair<int,int>(7,7));
        map1.insert(pair<int,int>(1,1));
        map <int,int> ::iterator itor;
    
        for(itor=map1.begin();itor!=map1.end();itor++)
            cout<<itor->first<<itor->second<<endl;
        itor=map1.find(5);
        if(itor!=map1.end())
            cout<<"Exists"<<endl;
        map1.size();
        map1.empty();


    
    
    五、多线程和网络编程:
               1. 原子锁 自旋锁 信号量 相互排斥锁:
                       自旋锁专门为多处理器并发而引入的一种锁,在内核中大量应用于中断处理等部分。最多仅仅能被一个内核任务持有,假设一个内核任务试图请求一个已经被占用的自旋锁,那么这个任务就会一直进行忙循环。
                       信号量:用在多线程、多进程同步,假设线程完毕了某个操作就会通过信号量告诉别的线程,别的线程在进行某些动作。进程假设检測到信号量被占用,就会被发送到等待序列。信号量能够不是为了资源竞争使用,比方控制流程。
                       相互排斥锁:用在多线程同步,如竞争资源訪问控制。
               2. C++多线程和多进程:
               3. TCP三次握手过程:
                                
               4. socket编程基础:
                     server端 socket, bind, listen, accept. client端 socket, connect , send/receive。
               5. 短连接和长连接:
                       长连接时,client端向server端发起连接,server接受连接,两方建立连接。假设client/server完毕一次读写以后,他们之间的连接并未主动关闭,兴许操作能够继续使用这个连接。
               6. 多线程编程基础:
                       多线程之间能够通过共享内存的方式同步数据,或者使用TCP。多线程间同步能够使用信号量、相互排斥锁、临界区、事件。
               7. TCP与UDP的差别:
                       TCP是面向连接的,可靠的字节流服务。两方先建立TCP连接,然后才数据传输。TCP支持超时重发,丢弃反复数据,校检数据和流量控制。ftp
                       UDP是非面向连接的,不提供可靠性。它仅仅是把应用程序传给IP层的数据报先发送出去,并不能保证它们可以达到目的地。没有超市重发等机制,故而传输速度非常快。ping
                       TCP报文头的结构:源port、目的port、序列号、确认序列号、首部长度、窗体大小、紧急指针、选项、数据。UDP数据包显然没有上述的非常多数据。
               8. OSI7层模型:
                 
               9. C++实现单例模式:
    class Singleton{//太令我挣扎了。。。
    private:
        static Singleton *_instance;
        Singleton(){
        }
    public:
        static Singleton * Instance(){
            if(0==_instance)
                _instance=new Singleton;
            return _instance;
        }
        static Singleton * Get_Instance(){
            return _instance;
        }
    };
    
    Singleton* Singleton::_instance = 0;
    int main(){
        Singleton *sg = Singleton::Instance();
        return 0;
    }
               10. C++异常处理:

    參考文献:
    1. 进程内存空间: http://www.cnblogs.com/biyeymyhjob/archive/2012/07/14/2591714.html
    2. 原子操作、信号量、自旋锁、相互排斥锁: http://blog.163.com/hbu_lijian/blog/static/126129153201261722410353/
    3. 二叉树面试题目:http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html
    5. OSI 7层模型: http://blog.sina.com.cn/s/blog_6f83fdb40101fchk.html
    
    
  • 相关阅读:
    Linq之旅:Linq入门详解(Linq to Objects)【转】
    Shadow Map 原理和改进 【转】
    OSG 中文解决方案 【转】
    shadow mapping实现动态shadow实现记录 【转】
    RenderMonkey 练习 第六天 【OpenGL Water 水效】
    glsl水包含倒影的实现(rtt) 【转】
    Docker镜像仓库Harbor之搭建及配置
    docker登录没有配置https的harbor镜像仓库
    Git 清除远端已删除的分支
    单节点k8s的一个小例子 webapp+mysql
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4277879.html
Copyright © 2011-2022 走看看