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、知道为什么要人为写拷贝构造函数么?
- C++经典面试问题20-40
- c 面试经典
- C/C++面试经典
- c语言面试经典
- C++经典面试问题1-20
- 面试经典问题回答
- 面试的经典问题
- C# 经典面试问题。。。
- JAVA面试经典问题
- 经典面试问题
- 经典面试问题
- 经典面试问题答案
- 经典面试问题
- 面试的经典问题
- 面试经典问题
- 公司面试经典问题
- 软件工程师面试经典问题
- 经典面试问题
- 欢迎使用CSDN-markdown编辑器
- Merge Intervals
- 三大框架环境配置
- plsql
- AppCompatActivity与toolbar的结合,封装BaseActivity
- C++经典面试问题20-40
- Spring AOP基础知识学习——Annotation配置
- 求一元二次方程的根【double型的0输出%.2lf为-0.00】
- 验证手机号、用户名的正则表达式
- L3-001. 凑零钱(dp)
- Java输入输出流的使用详细介绍
- 设备与模块
- spring事物管理相关配置
- [bzoj 2588] Spoj 10628. Count on a tree:函数式线段树