C++经典面试问题20-40

来源:互联网 发布:ie11 找不到js 编辑:程序博客网 时间:2024/06/10 09:19

21、请说出static和const关键字尽可能多的作用

static关键字至少有下列n个作用:

(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

const关键字至少有下列n个作用:

(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:
const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:
classA a, b, c;
(a * b) = c; // 对a*b的结果赋值

操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。

22、编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:

class String{   public:   String(const char *str = NULL); // 普通构造函数   String(const String &other); // 拷贝构造函数   ~ String(void); // 析构函数   String & operate =(const String &other); // 赋值函数   private:   char *m_data; // 用于保存字符串};

解答:
//普通构造函数
String::String(const char *str){    if(str==NULL)//加分点:对m_data加NULL 判断    {        m_data = new char[1];         *m_data = ‘\0′;// 得分点:对空字符串自动申请存放结束标志’\0′的空    }    else{        int length = strlen(str);        m_data = new char[length+1]; // 若能加 NULL 判断则更好        strcpy(m_data, str);    }}

// String的析构函数

String::~String(void){    delete [] m_data; // 或delete m_data;}
//拷贝构造函数
String::String(const String &other) // 得分点:输入参数为const型{    int length = strlen(other.m_data);    m_data = new char[length+1]; //加分点:对m_data加NULL 判断    strcpy(m_data, other.m_data);}
//赋值函数
String & String::operate =(const String &other) // 得分点:输入参数为const型{    if(this == &other) //得分点:检查自赋值    return *this;    delete [] m_data; //得分点:释放原有的内存资源    int length = strlen( other.m_data );    m_data = new char[length+1]; //加分点:对m_data加NULL 判断    strcpy( m_data, other.m_data );    return *this; //得分点:返回本对象的引用}


23、多态的作用

主要是两个:
1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

24、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部 变量有什么区别?static函数与普通函数有什么区别?

static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;

static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

25、生命周期,作用域的定义;说明全局变量、静态变量、局部变量、const变量的生命周期、作用域

生命周期,作用域的定义;说明全局变量、静态变量、局部变量、const变量的生命周期、作用域:

生命周期:是一个变量存在的周期。

作用域:是一个变量可以被引用的范围。最常见的如:{}、static修饰符等等。

1)全局变量:

作用域:全局作用域(只需要在一个源文件中定义,就可以作用于所有的源文件);

生命周期:程序运行期一直存在;

引用方法:其他文件如果要使用,必须用extern 关键字声明要引用的全局变量;

内存分布:全局(静态存储区)。

注意:如果再两个文件中都定义了相同名字的全局变量,则连接错误:变量重定义。

2)全局静态变量:

生命周期:程序运行期一直存在;

作用域:文件作用域(只在被定义的文件中可见:static的一个作用就是隐藏)

内存分布:全局(静态存储区)。

定义方法:static关键字,const关键字(注意C/C++意义不同)

注意:只要文件不相互包含,两个不同的文件中是可以定义完全相同的两个全局静态变量的。

3)静态局部变量:

生命周期:程序运行期一直存在;(超过其作用域便无法被引用)

作用域:局部作用域(只在局部作用于可见)

内存分布:全局(静态存储区)。

定义方法:局部作用域中用static定义。

注意:只被初始化一次,多线程中需要加锁保护。

4)局部变量:

生命周期:程序运行处局部作用域 即被销毁。

作用域:局部作用域(只在局部作用于可见)

内存分布:栈区

定义方法:在局部作用域中用auto指示符定义,可省略auto

static int c =  1;//全局初始化去(静态全局变量)int a = 0; //全局初始化区(全局变量)char  *p1; //全局未初始化区(全局变量) int main(int argc, char const *argv[]){    int b;//存放于栈区(局部变量)    char s[] = "abc";//s存放于栈区(局部变量)    char *p2 ;//栈(局部变量)    char *p3 = "123456";//p3存放于栈区(局部变量);“123456”存放区文字常量区     static int c = 0;// c存放于全局初始化区(静态局部变量)    p1 = (char *)malloc(10);    p2 = (char *)malloc(20);//分配得来的10和20字节的区域在堆区;                            //变量p1、p2指向堆区分配的内存     strcpy(p1, "123456");//“123456”存放区文字常量区    return 0;}

26、内存分配有哪几种形式?分别为何?区别是什么?对编译速度影响是何? 

           内存分配有三种方式: 

(1)从静态存储区域分配, 内存在编译时就已分配好, 这块内存在程序整个运行期间都存在, 如:全局变量,STATIC变量. 

(2)在栈上分配, 如函数的局部变量可以在栈上分配,函数结束时自己被释放,栈内存分配的运算内置于处理器的指令集中,效率高,内存分配是连续的,,栈是向低地址扩展的数据结构,但容量有限; 

(3)从堆上分配,亦即动态内存分配, 程序运行时用New/Malloc分配, 程序员自己负责何时用Delete/Free释放, 若程序员不释放,程序结束时可能由操作系统回收.类似于链表,在内存中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的.一旦某一节点从链中断开,我们要人为的把所断开的节点从内存中释放.动态内存的生存期由我们决定,使用灵活(堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存),但问题也多 

申请效率的比较: 

栈由系统自动分配,速度较快。但程序员是无法控制的。 

堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 

27、struct 和class有什么区别?c语言中的struct 和c++中的struct一样么?有什么区别?

从语法上来讲,class和struct做类型定义时只有两点区别:

1、默认继承权限,如果不指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理;

2、成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。以上两点也是struct和class最基本的差别,也是最本质的差别;

但是在C++中,struct进行了扩展,现在它已经不仅仅是一个包含不同数据类型的数据结构了,它包括了更多的功能。Struct能包含成员函数,Struct有自己的构造函数,Struct可以有析构函数,Struct支持继承,Struct支持多态,Struct支持Private、Protected和Public关键字。

详细介绍:http://www.jb51.net/article/55877.htm

28、说说什么是野指针?野指针什么情况下出现?(没有初始化,delete后没有赋值为NULL)

29、你熟悉预编译指令么?条件编译是用来做什么的?你会写么?

预编译又称为预处理,是做些代码文本的替换工作

预编译又称为预处理,是做些代码文本的替换工作

处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等

就是为编译做的预备工作的阶段

主要处理#开始的预编译指令

预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。常见的预编译指令有:

(1)、#include 指令

该指令指示编译器将xxx.xxx文件的全部内容插入此处。若用<>括起文件则在系统的INCLUDE目录中寻找文件,若用" "括起文件则在当前目录中寻找文件。一般来说,该文件是后缀名为"h"或"cpp"的头文件。

注意:<>不会在当前目录下搜索头文件,如果我们不用<>而用""把头文件名扩起,其意义为在先在当前目录下搜索头文件,再在系统默认目录下搜索。

(2)、#define指令

该指令有三种用法:

第一种是定义标识,标识有效范围为整个程序,形如#define XXX,常与#if配合使用;

第二种是定义常数,如#define max 100,则max代表100(这种情况下使用const定义常数更好,原因见注1);

第三种是定义"函数",如#define get_max(a, b) ((a)>(b)?(a):(b)) 则以后使用get_max(x,y)就可以得到x和y中较大的数(这种方法存在一些弊病,见注2)。

第四种是定义"宏函数",如#define GEN_FUN(type) type max_##type(type a,type b){return a>b?a:b;} ,使用时,用GEN_FUN(int),则此处预编译后就变成了 max_int(int a,int b){return a>b?a:b;},以后就可以使用max_int(x,y)就可以得到x和y中较大的数.比第三种,增加了类型的说明。

(3)、#if、#else和#endif指令

这些指令一般这样配合使用:

#if defined(标识) //如果定义了标识

要执行的指令

#else

要执行的指令

#endif

在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:

#if !(defined XXX) //XXX为一个在你的程序中唯一的标识符,

//每个头文件的标识符都不应相同。

//起标识符的常见方法是若头文件名为"abc.h"

//则标识为"abc_h"

#define XXX

真正的内容,如函数声明之类

#endif

一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”(conditional compile)。

#if、#else、#elif和#endif指令

30、strcpy函数的实现

已知strcpy函数的原型是:

char *strcpy(char *dst, const char *src);

实现strcpy函数

解释为什么要返回char *

假如考虑dst和src内存重叠的情况,strcpy该怎么实现

char * strcpy(char *dst,const char *src)   //[1]{    assert(dst != NULL && src != NULL);    //[2]    char *ret = dst;  //[3]    while ((*dst++=*src++)!='\0'); //[4]    return ret;}

31、知道断言ASSERT()怎么样么?一定要常用。它是函数还是宏?为什么不能是函数?

assert不仅仅是个报错函数,事实上,它居然是个宏,并且作用并非“报错”。

  在经过对其进行一定了解之后,对其作用及用法有了一定的了解,assert()的用法像是一种“契约式编程”,在我的理解中,其表达的意思就是,程序在我的假设条件下,能够正常良好的运作,其实就相当于一个if语句:

if(假设成立){     程序正常运行;}else{      报错&&终止程序!(避免由程序运行引起更大的错误)  }

32、懂什么是链表么?会链表的一些基本操作么?

#include<stdio.h>  #include<stdlib.h>    typedef struct node  {      int data;      node* pNext;  }Node;    //链表的操作,以有头节点为例,无头节点类似  Node* head = NULL;    //创建链表,头结点data=0,pNext=NULL;  bool createNodeList()  {      head = (Node*) malloc(sizeof(Node));      if(NULL == head)      {          return false;      }      else      {          head->data = 0;          head->pNext = NULL;          return true;      }  }    //增加节点  bool addNode(Node* node)  {      if(NULL == head)      {          return false;      }      Node* p = head->pNext;      Node* q = head;      while(NULL != p)      {          q = p;          p = p->pNext;      }      q->pNext = node;      node->pNext = NULL;      return true;      }    //删除节点  bool deleteNode(int index)  {      if(NULL == head)      {          return false;      }      Node* p = head->pNext;            int length = 0;      while(NULL != p)      {          length ++;          p = p->pNext;      }        if(length < index)      {          return false;      }      else      {          Node* q = head;          p = head;          for(int i=0;i<index;i++)          {              q = p;              p = p->pNext;          }          Node* t = p->pNext;          q->pNext = t;          free(p);          return true;      }  }    //逆序  void reverseNodeList()  {      if(NULL == head)      {          return;      }      //如果链表长度为1      if(head->pNext == NULL)      {          return;      }      Node* p = head->pNext;      Node* q = p->pNext;      Node* t = NULL;      while(NULL != q)      {          t = q->pNext;          q->pNext = p;          p = q;          q = t;      }      head->pNext->pNext = NULL;      head->pNext = p;  }    //排序(降序)  void sort()  {      //冒泡排序      Node* pHead = head;      if(head == NULL)      {          return;      }      if(pHead->pNext == NULL)      {          return;      }      Node* pi = pHead->pNext;      Node* pj = pi->pNext;      for(;pi != NULL;pi=pi->pNext)      {          for(pj = pi->pNext;pj != NULL;pj=pj->pNext)          {              if(pj->data>pi->data)              {                  int tmp = pj->data;                  pj->data = pi->data;                  pi->data = tmp;              }          }      }  }  //销毁  void destroyNodeList()  {      if(NULL == head)      {          return;      }      if(NULL == head->pNext)      {          free(head);          head = NULL;          return;      }      Node* p = head->pNext;      while(NULL != p)      {          Node* tmp = p;          p = p->pNext;          free(tmp);      }      free(head);      head = NULL;  }    void main()  {      createNodeList();        Node* node1 = (Node*)malloc(sizeof(Node));      node1->data = 1;      node1->pNext = NULL;        Node* node2 = (Node*)malloc(sizeof(Node));      node2->data = 2;      node2->pNext = NULL;        addNode(node1);      addNode(node2);        reverseNodeList();        Node* node3 = (Node*)malloc(sizeof(Node));      node3->data = 3;      node3->pNext = NULL;        addNode(node3);        sort();        deleteNode(2);            destroyNodeList();  }  

33、明白队列,双链表,循环链表,栈是怎么回事?会写这些类。

34、一定要知道二叉树的遍历有几种,一定要会写用递归的方式来遍历它们。
35、在一个字符串中,你能计算里边“数字字符”的个数么?
36、知道sizeof(Type*)=4吗?注:win32下
37、

int array[5]= {}; sizeof(array)=?int Fuction(int a[]){return sizeof(a);}Fuction(array)=?

38、你知道函数不能返回栈指针么?
39、知道局部变量为什么比全局变量快么?
40、知道为什么要人为写拷贝构造函数么?











0 0
原创粉丝点击