1、c++是不是类型安全的?
不是。两个不同类型的指针之间可以强制转换(用reinterpretcast)。C#是类型安全的。
2、文件头包含
(1)头文件中的ifndef/define/endif的作用?
防止该头文件被重复引用。
(2)include<file.h> 与 #i nclude "file.h"的区别?
前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。
3、mian执行前,还会执行什么代码?
全局对象的构造函数会在main函数之前执行;
4、局部变量
将局部便令尽可能置于最小作用域内,在声明变量时将其初始化。C++允许在函数的任何位置声明变量,离第一次使用越近越好,而且应该使用初始化代替声明+赋值的方式。
int i ;
i = f() ;//初始化和声明分离
int i = f() ; //初始化时声明
5、0与NULL
整数用0,实数用0.0,指针用NULL,字符串用‘\0’
答案:
BOOL : if ( !a )or if(a)
int : if ( a ==0)
float : constEXPRESSION EXP = 0.000001
if ( a < EXP&& a >-EXP)
pointer : if ( a != NULL) or if(a == NULL)6、sizeof
尽量使用sizeof(varname)代替sizeof(type):因为使用sizeof(varname)是因为变量类型改变时代码自动同步,有些情况下sizeof(type)或许还有意义,但是尽量避免,因为如果变量改变不能同载(overload)和重写(overried)的区别?
7、重载和重写的区别
从定义上来说:
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义复类虚函数的方法。---虚函数的实现
从实现原理上来说:
重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。8、内存分配问题
从静态存储区域分配:内存在程序编译的时候就已经分配好了,这块内存在程序整个运行期间都存在,如全局变量,static变量。
在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时,这些存储单元都自动释放。栈内存分配运算置于处理器的指令集中,效率很高,但是分配的内存容量有限。
从堆上分配(动态内存分配)程序在运行的时候用malloc或者new申请任意多少的内存,程序员负责在何时用free或delete释放内存。动态内存的生存期自己决定,很灵活。
9、动态数组
申请的时候,从外向内,逐级申请;
释放的时候,从内向外,逐级释放;c中:
申请:
array = (int***)malloc(n1*sizeof(int**)) ;
for(int i = 0 ; i < n1 ; i++)
{
array[i] = (int***)malloc(n2*sizeof(int*)) ;
for( int j = 0 ; j < n2 ; j++)
{
array[i][j] = (int*)malloc(n3*sizeof(int)) ;
}
}
释放:
for(int i = 0 ; i < n2 ; i++)
free(array[i][j]) ;
for( int i = 0 ; i < n1 ;i++)
free(array[i]);
free array ;
c++中有两种方法,一是new-delete;一种是使用vector,推荐使用vector
一维:
Int *p = new int[len] ; //new开辟新的空间之后,会返回这段内存的首地址;
Delete[] p ;
二维:
Int **p = new int*[n1] ;
For(int I = 0 ;i< n1 ; i++)
P[i] = new int[n2] ;
For(int I = 0 ;i< n1 ; i++)
Delete[] P[i] ;
P[i] = NULL ;
Delete[] p ;
使用vector:
一维:
Vector<int> array(len) ;
二维:
Vector< vector<int> > vecint(m ,vector<int>(n)); m行,n列
Vecint[i][j]
三维:
Vector< vector<vector<int> > > vecint(m ,vector< vector< int> >( n ,vector<int>(l) ) ) ;
Vecint[i][j][l]
10、inline会一直展开么?
不一定,看编译器。
Inline对编译器来说只是一种建议,编译器可以选择忽略这个建议。比如,如果你将一个长达1000多行的函数指定为inline,编译器就会忽略这个inline,将这个函数还原为普通函数。
调用inline函数的时候,要保证内联函数的定义让编译器看到,也就是说内联函数的定义要在头文件中,这与通常的函数定义不太一样。但是如果你习惯将函数定义放在cpp文件中,或者想让头文件看起来简单一点,可以这样做:
//someInLine.h
#ifndef SOMEINLINE_H
Inline Type Example(void);
//…..其它的函数声明
#include “someInLine.cpp”
#endif
// someInLine.cpp
#include “someInLine.h”
Type Example(void)
{
//….
}
11、malloc/free与new/delete
malloc/free是c/c++语言的标准库函数,new/delete是c++运算符。对于非内部数据类的对象而言,光用malloc/free不能满足动态对象的要求
new自动计算需要分配的空间,malloc需要手动计算;
new是类型安全的,malloc不是
new将调用constructor,而malloc不用;delete调用destructor,而free不能;
new/delete不需要库支持;malloc/free需要;
new/delete完全覆盖了malloc/free,为什么还需要malloc/free呢?
因为c++程序经常要调用c函数,而c程序只能用malloc/free管理动态内存;如果用free释放new,不能析构出错;用delete释放“malloc申请的动态内存”,理论上讲不会出错,但是程序可读性会变差。所以两者一般配对使用。
PS: Delete只会调用一次析构函数;
Delete[] 会调用每一个成员的析构函数;
12、基类的析构函数为什么要为虚函数?
如果基类的虚函数析构为虚函数,派生类在析构的生活,会首先析构派生类,再析构基类;如果基类的析构函数不是虚的,那么当删除对象的时候,就不能析构整个析构链。
13、尽可能使用const
任何可以使用const的情况下都要使用const;在声明变量或参数前面加上关键字const用于指明变量值不可修改,为类中的函数加上const限定表明该函数不会修改类成员变量的状态。Const变量、数据成员、函数、参数为编译时类型检查加上了一层保障,更好的尽早发现错误,强烈建议在一下几种情况下使用const:
- 如果函数不会修改传入的引用或者指针类型的参数,这时参数用该是const类型;
- 尽可能将函数声明为const,访问函数应该总是const,其他函数如果不会修改任何数据成员也应该是const,不要调用非const函数,不要返回对数据成员的非const指针或者引用;
- 如果数据成员在对象构造之后不再改变,也将其定义为const;
14、值传递与引用
可参考:http://blog.youkuaiyun.com/wfwd/archive/2006/05/30/763551.aspx
15、迭代器
使用迭代器时,通常可以编写程序使得要求迭代器有效的代码范围相对较短。然后,在该范围内,严格检查每一条语句,判断是否有元素添加或删除,从而相应地调整迭代器的值。
任何insert 或 push 操作都可能导致迭代器失效。当编写循环将元素插入到 vector 或deque 容器中时,程序必须确保迭代器在每次循环后都得到更新。resize 操作可能会使迭代器失效。在 vector 或 deque 容器上做resize 操作有可能会使其所有的迭代器都失效。对于所有的容器类型,如果 resize 操作压缩了容器,则指向已删除的元素迭代器失效。