zoukankan      html  css  js  c++  java
  • C++阶段性总结

      有时我们希望某些常量只在类中有效。由于#define定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用const修饰数据成员来实现。const数据成员的确是存在的,但其含义却不是我们所期望的。const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。

        不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。

        class A

        {…

            const int SIZE = 100;     // 错误,企图在类声明中初始化const数据成员

            int array[SIZE];        // 错误,未知的SIZE

        };

    const数据成员的初始化只能在类构造函数的初始化表中进行,例如

        class A

        {…

            A(int size);     // 构造函数

            const int SIZE ;

        };

        A::A(int size) : SIZE(size)    // 构造函数的初始化表

        {

        …

        }

        A a(100); // 对象 a 的SIZE值为100

        A b(200); // 对象 b 的SIZE值为200

    1.new、delete、malloc、free关系

        delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。

    2.与 delete []区别

      delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。

    3.有哪些性质(面向对象特点)

     封装,继承和多态。

    4.子类析构时要调用父类的析构函数吗?

       析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数

      16. C++中的class和struct的区别

    从语法上,在C++中(只讨论C++中)。class和struct做类型定义时只有两点区别:
    (一)默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理;
    (二)成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。
    除了这两点,class和struct基本就是一个东西。语法上没有任何其它区别。

    求下面函数的返回值(微软)

    int func(x) 
       { 
           int countx = 0; 
          while(x) 
          { 
              countx ++; 
              x = x&(x-1); 
           } 
          return countx; 
      } 

    假定x = 9999。 答案:8

    思路:将x转化为2进制,看含有的1的个数。

    5:什么是“引用”?申明和使用“引用”要注意哪些问题?

      引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。

    6:“引用”与多态的关系?

      引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例

    a)

    #include <stdio.h>
    union
    {
    int i;
    char x[2];
    }a;


    void main()
    {
    a.x[0] = 10; //2进制就是0000 1010(0AH
    a.x[1] = 1 ; //2进制就是 0000 0001(01H
    printf("%d",a.i); //结果就是 0000 0001 0000 1010=266
    }
    答案:266 (低位低地址,高位高地址,内存占用情况是Ox010A)

    b)

    main() 
         { 
              union

    {                   /*定义一个联合*/ 
                   int i; 
                   struct

           {             /*在联合中定义一个结构*/ 
                        char first; 
                        char second; 
                   }half; 
              }number; 


              number.i=0x4241;         /*联合成员赋值*/ 
              printf("%c%cn", number.half.first, mumber.half.second); 
              number.half.first='a';   /*联合中结构成员赋值*/ 
              number.half.second='b'; 
              printf("%xn", number.i); 
              getch(); 
         } 
    答案: AB   (0x41对应'A',是低位;Ox42对应'B',是高位)

           6261 (number.i和number.half共用一块地址空间) 

    9:多态的作用?

    主要是两个:

    1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;

    2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

    12. 描述内存分配方式以及它们的区别?

    1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
    2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
    3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

    BOOL :    if ( !a ) or if(a)
    int :     if ( a == 0)
    float :             if ( a < 0.00001&& a >-0.00001)
    pointer : if ( a != NULL) or if(a == NULL)

    14:请说出const与#define 相比,有何优点?

    答案:

    Const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

    1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
          2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

      

    15:简述数组与指针的区别?

       数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

    16.类成员函数的重载、覆盖和隐藏区别?

    答案:a.成员函数被重载的特征:
    (1)相同的范围(在同一个类中);
    (2)函数名字相同;
    (3)参数不同;
    (4)virtual 关键字可有可无。
    b.覆盖是指派生类函数覆盖基类函数,特征是:
    (1)不同的范围(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有virtual 关键字。
     c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
    (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
    (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

    17.求出两个数中的较大值

    There are two int variables: a and b, don’t use “if”, “? :”, “switch”or other judgement statements, find out the biggest one of the two numbers.

    答案:( ( a + b ) + abs( a - b ) ) / 2

    19.如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

    答案:
    #ifdef __cplusplus
    cout<<"c++";
    #else
    cout<<"c";
    #endif

    20:堆栈溢出一般是由什么原因导致的?

    答 、1.没有回收垃圾资源

            2.层次太深的递归调用 

    21:什么函数不能声明为虚函数?

    答 、constructor

    22.用两个栈实现一个队列的功能?要求给出算法和思路!

    答 、设2个栈为A,B, 一开始均为空.

        入队:

         将新元素push入栈A;

       出队:

        (1)判断栈B是否为空;

        (2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;

        (3)将栈B的栈顶元素pop出;

    23.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

    #define MIN(A,B) ((A) <= (B)? (A) : (B))

    24.交换两个数的宏定义

    交换两个参数值的宏定义为:. #define SWAP(a,b) (a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);

    25.用变量a给出下面的定义

    a) 一个整型数(An integer)

    b) 一个指向整型数的指针(A pointer to an integer)

    c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)

    d) 一个有10个整型数的数组(An array of 10 integers)

    e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)

    f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)

    g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)

    h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer

    argument and return an integer )

    答案是:

    a) int a; // An integer

    b) int *a; // A pointer to an integer

    c) int **a; // A pointer to a pointer to an integer

    d) int a[10]; // An array of 10 integers

    e) int *a[10]; // An array of 10 pointers to integers

    f) int (*a)[10]; // A pointer to an array of 10 integers

    g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer

    h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

     26:关键字static的作用是什么?

    这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

    1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

    2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

    3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

    27:.下面的代码输出是什么,为什么?

    void foo(void)

    {

    unsigned int a = 6;

    int b = -20;

    (a+b > 6) puts("> 6") : puts("<= 6");

    }  

    这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是“>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6

    28:.C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

    int a = 5, b = 7, c;

    c = a+++b;

    因此, 这段代码持行后a = 6, b = 7, c = 12。

    29:.用递归算法判断数组a[N]是否为一个递增数组。

    递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束:

    bool fun( int a[], int n )

    {

    if( n= =1 )

    return true;

    if( n= =2 )

    return a[n-1] >= a[n-2];

    return fun( a,n-1) && ( a[n-1] >= a[n-2] );

    }

    18.如何打印出当前源文件的文件名以及源文件的当前行号?

    答案:
    cout << __FILE__ ;
    cout<<__LINE__ ;
    __FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。 

    char a[] = "hello world";
           char *p = a;
           cout<< sizeof(a) << endl; // 12 字节,结束符号'/0'也要计算在里面
           cout<< sizeof(p) << endl; // 4 字节
        计算数组和指针的内存容量

        注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
          void Func(char a[100])
          {
               cout<< sizeof(a) << endl; // 4 字节而不是100 字节
          } 

    19:分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。

    20:引用”与指针的区别是什么?

       指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。

    21.结构与联合有和区别?

          (1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 
       (2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。 

    22.     析构函数和虚函数的用法和作用

    析构函数是在对象生存期结束时自动调用的函数,用来释放在构造函数分配的内存。

    虚函数是指被关键字virtual说明的函数,作用是使用C++语言的多态特性

    23:  Windows程序的入口是哪里?写出Windows消息机制的流程

    Windows程序的入口是WinMain()函数。            

    Windows应用程序消息处理机制:

    A. 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中

    B. 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。

    C. 应用程序调用DispatchMessage,将消息回传给操作系统。

    D. 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。

    宏与内联函数的区别

        内联函数和宏都是在程序出现的地方展开,内联函数不是通过函数调用实现的,是在调用该函数的程序处将它展开(在编译期间完成的);宏同样是;  

       不同的是:内联函数可以在编译期间完成诸如类型检测,语句是否正确等编译功能;宏就不具有这样的功能,而且宏展开的时间和内联函数也是不同的(在运行期间展开)

    47.如何判断一个单链表是有环的?(注意不能用标志位,最多只能用两个额外指针)

    struct node { char val; node* next;}
             bool check(const node* head) {} //return false : 无环;true: 有环一种O(n)的办法就是(搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重合,反之亦然):
    bool check(const node* head)
    {
        if(head==NULL)  return false;
        node *low=head, *fast=head->next;
        while(fast!=NULL && fast->next!=NULL)
        {
            low=low->next;
            fast=fast->next->next;
            if(low==fast) return true;
        }
        return false;
    }

    48.指针找错题

    分析这些面试题,本身包含很强的趣味性;而作为一名研发人员,通过对这些面试题的深入剖析则可进一步增强自身的内功。
      2.找错题

    试题1:
    以下是引用片段:
    void test1()  //数组越界
      {
      char string[10];
      char* str1 = "0123456789";
      strcpy( string, str1 );
      }
      试题2: 
    以下是引用片段:
     void test2()
      {
      char string[10], str1[10]; 
      int i;
      for(i=0; i<10; i++)
      {
      str1= 'a';
      }
      strcpy( string, str1 );
      }
      试题3:  
    以下是引用片段:
    void test3(char* str1)
      {
      char string[10];
      if( strlen( str1 ) <= 10 )
      {
      strcpy( string, str1 );
      }
      }
      解答:
      试题1字符串str1需要11个字节才能存放下(包括末尾的’/0’),而string只有10个字节的空间,strcpy会导致数组越界;对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string,str1)调用使得从 str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10分;
    对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) <10),因为strlen的结果未统计’/0’所占用的1个字节。剖析:考查对基本功的掌握
      (1)字符串以’/0’结尾;
      (2)对数组越界把握的敏感度;
      (3)库函数strcpy的工作方式,

    试题4: void GetMemory( char *p )
      {
      p = (char *) malloc( 100 );
      }
      void Test( void )
      {
      char *str = NULL;
      GetMemory( str );
      strcpy( str, "hello world" );
      printf( str );
       } 
      试题5:   char *GetMemory( void )
      {
      char p[] = "hello world";
      return p;
      }
      void Test( void )
      {
      char *str = NULL;
      str = GetMemory();
      printf( str );
      }
      试题6:void GetMemory( char **p, int num )
      {
      *p = (char *) malloc( num );
      }
      void Test( void )
      {
      char *str = NULL;
      GetMemory( &str, 100 );
      strcpy( str, "hello" );
      printf( str );
      }
      试题7:以下是引用片段:
     void Test( void )
      {
      char *str = (char *) malloc( 100 );
      strcpy( str, "hello" );
      free( str );
      ... //省略的其它语句
      }
      解答:试题4传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
      char *str = NULL; 
      GetMemory( str );
      后的str仍然为NULL;试题5中
      char p[] = "hello world";
      return p;
      的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
      试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句 tiffanybracelets
      *p = (char *) malloc( num );
      后未判断内存是否申请成功,应加上:
      if ( *p == NULL )
      {
      ...//进行申请内存失败处理

      }
      试题7存在与试题6同样的问题,在执行
      char *str = (char *) malloc(100);
      后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:
      str = NULL;
      试题6的Test函数中也未对malloc的内存进行释放。
      剖析:
      试题4~7考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。

    软件开发网 www.mscto.com
      对内存操作的考查主要集中在:
      (1)指针的理解;
      (2)变量的生存期及作用范围;
      (3)良好的动态内存申请和释放习惯。
      再看看下面的一段程序有什么错误:  
    以下是引用片段:
    swap( int* p1,int* p2 )
      {
      int *p;
      *p = *p1;
      *p1 = *p2;
      *p2 = *p;
      }
      在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为
    以下是引用片段:
    swap( int* p1,int* p2 )
      {
      int p;
      p = *p1;
      *p1 = *p2;
      *p2 = p;
      }

     101.用递归算法判断数组a[N]是否为一个递增数组。

    递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束:

    bool fun( int a[], int n )

    {

    if( n= =1 )

    return true;

    if( n= =2 )

    return a[n-1] >= a[n-2];

    return fun( a,n-1) && ( a[n-1] >= a[n-2] );

    }

    如何定义和实现一个类的成员函数为回调函数?

    typedef   void   (*FunPtr)(void);   
      class   A   
      {   
         public   :   
         static   void     callBackFun(void   )   
        {   
          cout<<"callBackFun   "<<endl;   
        }   
      };

    void   Funtype(   FunPtr   p)   
      {   
          p();   
      }  

    void     main(void)   
      {   
          Funtype(A::callBackFun);   
      }

  • 相关阅读:
    常用词汇短语
    Java中的数据结构
    Java中的设计模式
    .NET中的编译、程序调用
    常用口语
    0. Angular框架原理
    茶叶
    NPOI自定义单元格背景颜色
    ASP.NET MVC添加Action就找不到
    navicat for mysql 导入SQL Server显示中文乱码解决办法
  • 原文地址:https://www.cnblogs.com/renyuan/p/2837185.html
Copyright © 2011-2022 走看看