C++常见面试问题

1.struct和union

--union中只存放了一个被选中的成员;而struct的所有成员都存在

--struct变量的总长度等于所有成员长度之和;Union变量的长度等于最长的成员的长度。

2.static和const

--static:

修饰变量:

a.static修饰局部变量,它就改变了局部变量的存储位置(从原来的栈中存放改为静态存储区)及其生命周期(局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能进行访问),但未改变其作用域。 

b.static修饰全局变量,并为改变其存储位置及生命周期,而是改变了其作用域,使当前文件外的源文件无法访问该变量,好处如下:(1)不会被其他文件所访问,修改(2)其他文件中可以使用相同名字的变量,不会发生冲突。对全局函数也是有隐藏作用。

修饰类成员:

a.static修饰类的成员变量:实际使其成为类的全局变量,会被类的所有对象共享,包括派生类的对象。因此,static成员必须在类外进行初始化(初始化格式: int base::var=10;),而不能在构造函数内进行初始化。

b.static修饰类的成员函数:(1)使这个类只存在这一份函数,所有对象共享该函数,不含this指针;(2)静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。当static成员函数在类外定义时不需要加static修饰符。

--const

a.修饰变量变量不可以被修改;

b.修饰指针:char * const p【常指针】  ;;;const char *p【指向常量的指针变量】

3.指针与引用

--指针是变量,引用是别名

--指针可以为null,引用不可以为空且必须初始化

--指针可以改变,引用只能一直引用一个

4.重载(overload)、重写(override)、覆盖(overwrite)

--overload

--override: 继承体系中,子类实现父类虚函数

--overwrite:也是在继承体系中,子类覆盖了父类的方法

5.深拷贝与浅拷贝

--浅拷贝:默认的复制构造函数只是完成了对象之间的位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

--深拷贝:自定义复制构造函数需要注意,对象之间发生复制,资源重新分配,即A有5个空间,B也应该有5个空间,而不是指向A的5个空间。

6.虚继承和虚基类

--在多重继承(菱形继承)下,虚继承保证了最终的派生类只有基类的一份拷贝。而在根部的基类称为虚基类

class istream : public virtual ios { ... };
class ostream : virtual public ios { ... };
// iostream inherits only one copy of its ios base class
class iostream: public istream, public ostream { ... };

7.排序算法

8.容器及特点

 

9.TCP为什么要三次握手而不是两次:防止失效的请求又到达服务器,产生错误。

三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的

--TCP可靠传输实现:

TCP 连接的每一端都必须设有两个窗口——发送窗口和接收窗口。TCP 的可靠传输机制用字节的序号进行控制。TCP 所有的确认都是基于序号而不是基于报文段。
发送过的数据未收到确认之前必须保留,以便超时重传时使用。发送窗口没收到确认不动,和收到新的确认后前移。

发送缓存用来暂时存放: 发送应用程序传送给发送方 TCP 准备发送的数据;TCP 已发送出但尚未收到确认的数据。

接收缓存用来暂时存放:按序到达的、但尚未被接收应用程序读取的数据; 不按序到达的数据。

 --TCP报文格式

(1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  (2)确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
  (3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
    (A)URG:紧急指针(urgent pointer)有效。
    (B)ACK:确认序号有效。
    (C)PSH:接收方应该尽快将这个报文交给应用层。
    (D)RST:重置连接。
    (E)SYN:发起一个新连接。
    (F)FIN:释放一个连接。

 需要注意的是:
  (A)不要将确认序号Ack与标志位中的ACK搞混了。
  (B)确认方Ack=发起方Req+1,两端配对。

10.线程和进程,线程可以共享进程里的哪些东西。 知道协程是什么吗

--线程共享进程的地址空间,全局变量(数据和堆)。在一个进程中,各个线程共享堆区,而进程中的线程各自维持自己的栈。

--协程:

定义:协程其实可以认为是比线程更小的执行单元。为啥说他是一个执行单元,因为他自带CPU上下文。

11. makefile

--makefile关系到了整个工程的编译规则makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

--makefile成为了一种在工程方面的编译方法,其本质都是在“文件依赖性”上做文章

--源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File.

12. 大端小端模式

--大端:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。

--小端:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

http://www.cnblogs.com/LUO77/p/5771237.html

===================================================================

13. new/delete与malloc/free的区别

new/delete malloc/free
c++操作符 c库函数
new调用构造函数,delete调用析构函数 只对内置类型有效
new返回指定类型的指针,并自动计算大小
string *ps=new string(10,’9’)
malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针
 在创建对象的时候就可以初始化
malloc 只管分配内存,并不能对所得的内存进行初始化
new分配失败时,返回什么?
1993年前,c++一直要求在内存分配失败时operator new要返回0,现在则是要求operator new抛出std::bad_alloc异常。很多c++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator new(以及operator new[])以继续提供返回0功能。这些形式被称为“无抛出”,因为他们没用过一个throw,而是在使用new的入口点采用了nothrow对象: 
class   widget   {   ...   }; 
widget   *pw1   =   new   widget;//   分配失败抛出std::bad_alloc   
if   (pw1   ==   0)   ... //   这个检查一定失败 
widget   *pw2   =   new   (nothrow)   widget;   //   若分配失败返回0
if   (pw2   ==   0)   ... //   这个检查可能会成功

14.多态,虚函数,纯虚函数

多态:

--编译时多态:函数重载

--运行时多态:继承和虚函数

虚函数:提供一个接口和一份默认的实现

纯虚函数:提供一份接口(抽象类)

15.引用

-作为参数:

----直接对传入的对象进行修改;(传指针的时候需要在使用前对指针进行检查)

----为了降低复制自定义对象的开销,通常加上const;

-作为返回值(在内存中不产生被返回值的副本):

----返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!

----

16.栈内存与文常量区

      char str1[] = "abc";
  char str2[] = "abc";

  const char str3[] = "abc";
  const char str4[] = "abc";

  const char *str5 = "abc";
  const char *str6 = "abc";

  char *str7 = "abc";
  char *str8 = "abc";

  cout << ( str1 == str2 ) << endl;//0  分别指向各自的栈内存
  cout << ( str3 == str4 ) << endl;//0  分别指向各自的栈内存
  cout << ( str5 == str6 ) << endl;//1指向文字常量区地址相同

  cout << ( str7 == str8 ) << endl;//1指向文字常量区地址相同

  结果是:0 0 1 1

  解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

17.复杂声明

http://blog.csdn.net/zjc156m/article/details/16819357

=====================================================================

18.C++对象模型

non-static数据成员被放置到对象内部,static数据成员、static和nonstatic函数成员放到对象之外。

19.智能指针

   C++新标准库中定义了两种智能指针shared_ptr和unique_ptr,定义在memory头文件中。

shared_ptr:允许多个指针指向同一个对象;()

shared

--weak_ptr:是一种弱引用,指向shared_ptr所管理的对象 

--思想:引用计数为0就自动销毁所管理的对象,释放占用的内存

----拷贝一次,引用计数加1:

用shared_ptr初始化另一个shared_ptr

将其作为参数传递给另一个函数

作为函数返回值

----计数减1的情况:

给shared_ptr赋值一个新值

被销毁(离开局部作用域)

--操作:

p.get(),p.swap(q)(是共有的操作)、p.unique()、p.use_count()

--构造:

----auto p = make_shared_ptr<vector<string>>();

----shared_ptr<int> p(new int(1024));使用直接初始化形式,因为智能指针的构造函数是explicit的

unique_ptr:独占所指向的对象,不支持拷贝与赋值操作,但是可以通过release()和reset()操作将指针的控制权从一个unique转到另一个unique指针 

 20.虚函数

--静态函数不可以是虚函数:因为静态成员函数没有this,也就没有存放vptr的地方,同时其函数的指针存放也不同于一般的成员函数,其无法成为一个对象的虚函数的指针以实现由此带来的动态机制。静态是编译时期就必须确定的,虚函数是运行时期确定的。

--内联函数不可以是虚函数:inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数体现的是一种运行期机制。
因此,内联函数是个静态行为,而虚函数是个动态行为,他们之间是有矛盾的。

--设置虚函数须注意: 
1:只有类的成员函数才能说明为虚函数; 
2:静态成员函数不能是虚函数; 
3:内联函数不能为虚函数; 
4:构造函数不能是虚函数; 
5:析构函数可以是虚函数,而且通常声明为虚函数。

21.一个函数一旦声明为虚函数,那么不管你是否加上virtual 修饰符,它在所有派生类中都成为虚函数    

构造函数是不能被继承的,但是可以被调用,如果父类重新定义了构造函数,也就是没有了默认的构造函数,子类创建自己的构造函数的时候必须显式的调用父类的构造函数。而其余的三个在我们平常的使用中都可以被继承使用。                                                   22.静态局部变量只对定义它的函数体始终可见,函数体执行完过后虽然还存在,但是无法被其他的使用了                                  23.

-- public成员可以被类中的函数、友元函数、类的对象、派生类访问。

--私有继承时,基类的public和protected成员都变成派生类的private类型,因此不可被类外访问。
--保护继承时,基类的public和protected成员都变成派生类的protected类型,可以被派生类访问。
--protected成员可以被类中的函数、友元函数、派生类访问,不能被类的对象访问,因此不能在类外被访问
24.
基类对象与派生类对象之间存在赋值相容性,包括以下几种情况:
– 把派生类对象赋值给基类对象。
– 把派生类对象的地址赋值给基类指针。
– 用派生类对象初始化基类对象的引用。

 25.const

char* const p = &a;//常指针,指向不可以改变,数量可以改变

char const *p = &b;//指向常量的指针,b是常量

const修饰参数、返回值、成员函数 ,non-const 版本可以调用const版本,反之则不行。                                                       26.

volatile (容易改变的)作用是避免编译器优化,说明它是随时会变的,它不是 non-const,和const不矛盾。被const修饰的变量只是在当前作用范围无法修改,但是可能被其它程序修改。优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

27.

引用、const成员变量、基类构造函数一定要通过初始化列表来实现。 static类型不是类对象成员,不需要通过初始化列表来初始化                                                                                                                                                                                                                                                                                                                                                                             

 

posted @ 2017-03-13 23:29  lp3318  阅读(206)  评论(0编辑  收藏  举报