C++面试题精髓
1.、为什么构造函数不能声明为虚函数?
解析:因为虚函数采用的是虚调用的方法,虚调用是指允许在只知道部分信息的情况下
的工作机制,特别允许我们调用一个只知道接口而不知道其对象的准确类型的函数。
但是如果我们要调用构造函数创建对象时,必须要知道对象的准确类型,
因此构造函数不能为虚函数。
2.C++中哪些函数不能被声明为虚函数?
解析:普通函数(非成员函数),构造函数,内联成员函数、静态成员函数、友元函数。
(1)虚函数用于基类和派生类,普通函数所以不能
(2)构造函数不能是因为虚函数采用的是虚调用的方法,
(3)内联成员函数的实质是在调用的地方直接将代码扩展开
(4)继承时,静态成员函数不能被继承的,它只属于一个类,因为也不存在动态联编
(5)友元函数不是类的成员函数,因此也不能被继承
3.类的静态成员和非静态成员有何区别?
答:类的静态成员每个类只有一个,静态成员为所有类的实例对象共享,静态成员有静态成员变量和静态成员函数,静态成员变量使用前必须初始化,静态成员变量可以被静态成员函数和非静态成员函数访问,而静态成员函数只能访问静态成员变量,因为静态成员函数属于类,其没有this指针。非静态成员每个对象都有一个。
4 重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。 重写:是指子类重新定义复类虚函数的方法。 从实现原理上来说:重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func.对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关! 重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)
5 简述成员函数、全局函数和友元函数的差别。
成员函数只能由该类所实例化的对象来进行调用。[静态成员除外]
全局函数可以在任意位置进行调用。
友元函数可以让本类和友元类对象调用。
用new分配的内存用delete删除用new[]分配的内存用delete[]删除delete[]会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。
6.继承的优缺点。
类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。 (待补充)
7.C++有哪些性质(面向对象特点)
封装,继承和多态。
在面向对象程序设计语言中,封装是利用可重用成分构造软件系统的特性,它不仅支持系统的可重用性,而且还有利于提高系统的可扩充性;消息传递可以实现发送一个通用的消息而调用不同的方法;封装是实现信息隐蔽的一种技术,其目的是使类的定义和实现分离。
8. 在什么时候需要使用“常引用”?
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就 应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;
int a
const int &ra=a;
ra=1; //错误
a=1; //正确
例2 string foo( );
void bar(string & s);
那么下面的表达式将是非法的:
bar(foo( ));
bar("hello world");
原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const
9.ASSERT()是干什么用的
答:ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0),
程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,
10. 实现多态的方法?
解析: ① 一个基类的引用可以指向它的派生类实例
② 一个基类的指针可以指向它的派生类实例
11. 面向对象的三个基本特征,并简单叙述之?
① 封装:将客观事物抽象成类,每个类对自身的数据和方法实行访问控制(private, protected,public)
② 继承:广义的继承有三种实现形式:
实现继承(指使用基类的属性和方法而无需额外编码的能力)
可视继承(子窗体使用父窗体的外观和实现代码)
接口继承(仅使用属性和方法,实现滞后到子类实现)。
前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
③ 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
补充问题: 多态的作用?
主要是两个:
1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
12重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
① 重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
② 重写:是指子类重新定义父类虚函数的方法。
从实现原理上来说:
① 重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。对于这类函数的调用,在编译期间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
② 重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)
13多态的作用?
主要是两个:
① 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
② 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
14、C++中的空类,默认产生的类成员函数:
class { public: Empty(); // 缺省构造函数 Empty(const Empty&); // 拷贝构造函数 ~Empty(); // 析构函数 Empty& operator=(const Empty&); // 赋值运算符 Empty* operator&(); // 取值运算符 const Empty* operator&() const; // 取值运算符const };
15.进程间通信的方式有?
进程间通信的方式有 :共享内存, 管道(有名管道/无名管道),Socket ,消息队列 ,信号,信号量,内存映射等。
16死锁的四个必要条件?
互斥,请求保持,不可剥夺,环路。
17、类的静态成员和非静态成员有什么区别?
类的静态成员每个类只有一个,即是属于本类的;类的非静态成员每个对象都有一份。
18什么是浅拷贝?什么是深拷贝?
浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
一般来说,浅拷贝就是复制那个对象的指针。深拷贝就是复制了那个对象。
19、Windows编程线程同步的几种方式?(重要)
原子锁、临界区(段)、事件、互斥(体)、信号量、可等候定时器
20什么是“引用”?申明和使用“引用”要注意哪些问题?
答:引用就是某个目标变量的“别名”(alias),对引用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
21. “引用”与指针的区别是什么?
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传ref和pointer的区别。
22. 关联、聚合(Aggregation)以及组合(Composition)的区别?
涉及到UML中的一些概念:关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,如下图所示,用空的菱形表示聚合关系:
从实现的角度讲,聚合可以表示为:
class A {...} class B { A* a; .....}
而组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:
实现的形式是:
class A{...} class B{ A a; ...}
23.面向对象的三个基本特征,并简单叙述之?
1. 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected,public)
2. 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
3. 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
24 多态的作用?
主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
25 在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”声明?
//extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用
// extern “C是连接申明,编译时告诉编译器以下代码用C风格的方式编译和连接,其目的是实现C++与C及其它语言的混合编程。
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传ref和pointer的区别。
答案:都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.
24 成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
25覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
26如果用VC开发程序,常见这么几个错误,C2001,c2005,c2011,这些错误的原因是什么。
在学习VC++的过程中,遇到的LNK2001错误的错误消息主要为:
unresolved external symbol “symbol”(不确定的外部“符号”)。
如果连接程序不能在所有的库和目标文件内找到所引用的函数、变量或标签,将产生此错误消息。
一般来说,发生错误的原因有两个:一是所引用的函数、变量不存在、拼写不正确或者使用错误;
其次可能使用了不同版本的连接库。
编程中经常能遇到LNK2005错误——重复定义错误,其实LNK2005错误并不是一个很难解决的错误.
27介绍一下STL,详细说明STL如何实现vector。
STL (标准模版库,Standard Template Library)它由容器算法迭代器组成。
STL有以下的一些优点:
可以方便容易地实现搜索数据或对数据排序等一系列的算法;
调试程序时更加安全和方便;
即使是人们用STL在UNIX平台下写的代码你也可以很容易地理解(因为STL是跨平台的)。
vector实质上就是一个动态数组,会根据数据的增加,动态的增加数组空间。
28介绍一下模板和容器。如何实现?(也许会让你当场举例实现)
模板可以说比较古老了,但是当前的泛型编程实质上就是模板编程。
它体现了一种通用和泛化的思想。
STL有7种主要容器:vector,list,deque,map,multimap,set,multiset.
29 :简述多态实现的原理
编译器发现一个类中有虚函数,便会立即为此类生成虚函数表 vtable。虚函数表的各表项为指向对应虚函数的指针。编译器还会在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)指向虚函数表。调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable,将类与此类的vtable联系了起来。另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable,。如此才能真正与函数体进行连接,这就是动态联编,实现多态的基本原理。
30:谈谈你对面向对象的认识
解析:面向对象可以理解成对待每一个问题,都是首先要确定这个问题由几个部分组成,而每一个部分其实就是一个对象。然后再分别设计这些对象,最后得到整个程序。传统的程序设计多是基于功能的思想来进行考虑和设计的,而面向对象的程序设计则是基于对象的角度来考虑问题。这样做能够使得程序更加的简洁清晰。
说明:编程中接触最多的“面向对象编程技术”仅仅是面向对象技术中的一个组成部分。发挥面向对象技术的优势是一个综合的技术问题,不仅需要面向对象的分析,设计和编程技术,而且需要借助必要的建模和开发工具。
31 C++中为什么用模板类。
解析:
1) 可用来创建动态增长和减小的数据结构
2) 它是类型无关的,因此具有很高的可复用性。
3) 它在编译时而不是运行时检查数据类型,保证了类型安全
4) 它是平台无关的,可移植性
5) 可用于基本数据类型
32 函数模板与类模板有什么区别?
答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化
必须由程序员在程序中显式地指定。
33 .winsock建立连接的主要实现步骤? (非常重要,必问)
答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()
等待客户端连接。
客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(
),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连
接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock
et()关闭套接字。
34 进程和线程的差别。
答:线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
35. C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。
如果用VC开发程序,常见这么几个错误,C2001,c2005,c2011,这些错误的原因是什么。
37宏与内联函数的区别
解析:内联函数和宏都是在程序出现的地方展开,内联函数不是通过函数调用实现的,是在调用该函数的程序处将它展开(在编译期间完成的);宏同样是;
不同的是:内联函数可以在编译期间完成诸如类型检测,语句是否正确等编译功能;宏就不具有这样的功能,而且宏展开的时间和内联函数也是不同的(在运行期间展开)
38 win32中消息循环Windows程序的入口是哪里?写出Windows消息机制的流程
解析:Windows程序的入口是WinMain()函数。
Windows应用程序消息处理机制:
A. 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中
B. 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。
C. 应用程序调用DispatchMessage,将消息回传给操作系统。
D. 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。
39:谈谈你对编程规范的理解或认识
编程规范可总结为:程序的可行性,可读性、可移植性以及可测试性。
说明:这是编程规范的总纲目,面试者不一定要去背诵上面给出的那几个例子,应该去理解这几个例子说明的问题,想一想,自己如何解决可行性、可读性、可移植性以及可测试性这几个问题,结合以上几个例子和自己平时的编程习惯来回答这个问题。
40流操作符重载为什么返回引用
在程序中,流操作符>>和d50ff6c2bd31fcb4f08bbc4a5f139abe”
D. 三目运算符“? :”
解析:没有什么理由 ABD都不行
2. 在以下有关C++类的描述中,不正确的是
A.C++语言引入类主要起两个作用,作为数据封装机制和作为类型定义机制
B.C++类中定义的数据和函数分别称为数据成员和成员函数
C.C++类作为数据封装机制,将数据及作用于数据的操作组织在一起,实现了信息隐藏
D.C++类中的成员可分为四种:公有成员、保护成员、私有成员和友元
正确答案:D
3.设有int x = 123,语句 可产生“+123”形式的输出(“”代表空格)。
A.cout 3c64cfc9fcdb044593d4c351b7feebe9 class Sample {…};
B.template1bbcbf404aff658cf5eed197e6af2bc7 class Sample {…};
C.template8c49dd555a9806de561d35e83b4ae27d class Sample {…};
D.template17441c4bf03cd3139b4c9059754f3614 Sample {…};
正确答案:C
5非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?[C++中等]
a. class B : public A { ……}
b. class B { operator A( ); }
c. class A { A( const B& ); }
d. A& operator= ( const A& );
解析:第一项 B公有继承自A,可以是间接继承的。第二项: B实现了隐式转化为A的转化
第三项:A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数
第四项: 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个
6关于this指针的说法错误的是( A )
this指针必须显示说明
当创建一个对象后,this指针就指向该对象
成员函数拥有this指针
静态成员函数不拥有this指针
7.下面函数原型声明中,( B )声明了fun为纯虚函数
A . void fun()=0;
B virtual void fun()=0;
C virtual void fun();
D virtual void fun(){};
8友元运算符obj>obj2被C++编译器解释为( A )。
operator>(obj1,obj2)
>(obj1,obj2)
obj2.operator>(obj1)
obj1.oprator>(obj2)
9.假定AB为一个类,则执行“AB a(4),b[3],*p[2];”语句时,自动调用该类构造函数的次数为:B
A) 3 B) 4 C) 6 D) 9
10.假定要对类AB定义加号操作符重载成员函数,实现两个AB类对象的加法,并返回相加结果,则该成员函数的声明语句为:B
A) AB operator+(AB &a,AB &b)
B) AB operator+(AB &a)
C) operator+(AB a)
D) AB & operator+()
11.有二维字符数组char s[4][6]={"zhang","gao","tang","wang"};执行程序coutdaeeae8786145a0c6601e10ab13634c6打印结果是多少
using namespace std; class A1{ public: int a; static int b; A1(); ~A1(); }; int main() { cout << sizeof(A1) <<endl; }
解析:是4, 静态变量是存放在全局数据区的, 而sizeof是计算局部栈中分配的大小。
13.死锁的处理
解析:鸵鸟策略、预防策略、避免策略、检测与解除死锁,只要知道概念就行,不需要掌握
14. 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?[C++中等]
解析:a. class B : public A { ……} // B公有继承自A,可以是间接继承的
b. class B { operator A( ); } // B实现了隐式转化为A的转化
c. class A { A( const B& ); } // A实现non-explicit的参数为B(可有其他带默认值的参数)构造函数
d. A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个
15以下代码有什么问题?[STL易]
typedef vector IntArray;
IntArray array;
array.push_back( 1 );
array.push_back( 2 );
array.push_back( 2 );
array.push_back( 3 );
// 删除array数组中所有的2
for( IntArray::iterator itor=array.begin(); itor!=array.end(); ++itor )
{
if( 2 == *itor ) array.erase( itor );
}
解析:其实里面隐藏着一个很严重的错误:当veci.erase(iter)之后,iter就变成了一个野指针,对一个野指针进行 iter++ 是肯定会出错的。
16 下列代码的输出值是多少?
class A{};
class A1{};
class B : public A{};
class C : public A, public A1{};
class D : public virtual A{};
cout << sizeof ( A ) << endl;
cout << sizeof ( B ) << endl;
cout << sizeof ( C ) << endl;
cout << sizeof ( D ) << endl;
解析:答案:1, 1, 1,4说明:空类所占的空间为1,单一继承的空类空间也为1,多重继承的空类空间还是1, 但是虚继承涉及到虚表(虚指针),所以大小为4。
相关文章:
以上是C++精选面试题,一定要了解的详细内容。更多信息请关注PHP中文网其他相关文章!