C++面试题(二)
来源:互联网 发布:新的邮政软件 编辑:程序博客网 时间:2024/05/19 20:23
1、 C++和C有什么不同?
答:C是经典的结构化编程语言,C++兼容C,但是在其基础上增加了面向对象的思想,支持封装、继承、多态、重载和模板机制等。
2、 C也可以通过精心封装某些函数功能实现重用,那C++的类有什么优点吗(从面向对象的三大属性进行分析)
答:(1). 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected,public),而C不具备这种语法。
(2). 继承:派生类继承自基类,基类中拥有的数据派生类中也就拥有了,提高代码重用性,不需要再重新编写代码。
(3). 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
3、 class和struct 的区别?
答:在C++中,在class中声明的成员默认为private成员,而在struct中声明的成员默认为public成员。class的默认继承方式为private, struct默认继承方式为public;
4、 简述使用<iostream.h>与<iostream>命名空间std这两种形式有什么区别
答: <iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。
iostream.h是C的头文件库,iostream是C++标准头文件库,C++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。(但是因为早期C++继承了C的特性,为了兼容以前的C++代码,故保留了iostream.h的这种写法。早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,)目前后缀为.h的头文件c++标准已经明确提出不支持了(VC++8之后版本)。
因此,当使用<iostream.h>时,相当于在C中调用库函数,使用的是全局命名空间,也就是早期的C++实现;当使用< iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout.或是逐个定义如std::cout
5、 类与对象有什么区别?
答:类是自定义一种类型,相当于C语言的int类型等。
对象是类的实例,相当于C语言中的变量。
6、 面向对象的三个基本特征,并简单叙述之?
答:
1.封装:
封装是指利用抽象数据类型和基于数据的操作结合在一起,数据被保护在抽象数据类型的内部,系统的其他部分只有通过包裹在数据之外被授权的操作,才能与这个抽象数据类型进行交互。
2. 继承:
它是与传统方法不同的一个最有特色的方法。它是面向对象的程序中两个类之间的一种关系,即一个类可以从另一个类(即它的父类)继承状态和行为。继承父类的类称为子类。
继承的优越性:通过使用继承,程序员可以在不同的子类中多次重新使用父类中的代码,使程序结构清晰,易于维护和修改,而子类又可以提供一些特殊的行为,这些特殊的行为在父类中是没有的。
3.多态:
是指一个程序中同名的方法共存的情况,调用者只需使用同一个方法名,系统会根据不同情况,调用相应的不同方法,从而实现不同的功能。多态性又被称为“一个名字,多个方法”。
7、 new delete 与malloc free 的联系与区别?
答:
new delete和malloc free都是释放申请的堆上的空间,都是成对存在的,否则将会造成内存泄露或二次释放。不同的是,new delete是C++中定义的操作符,new除了分配空间外,还会调用类的构造函数来完成初始化工作,delete除了释放空间外还会调用类的析构函数。而malloc和free是C语言中定义的函数。
8、 描述内存分配方式以及它们的区别?
答:内存分配大致上可以分成5块:
1、 栈区(stack)。栈,就是那些由编译器在需要时分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。(由编译器管理)
2、 堆区(heap)。一般由程序员分配、释放,若程序员不是放,程序结束时可能由系统回收。注意,它与数据结构中的堆是两回事,分配方式类似于链表。
3、 全局区(静态区)(static)。全局变量和静态变量被分配到同一块内存中。程序结束后由系统释放。
4、 常量存储区。常量字符串就是放在这里的,不允许修改,程序结束后由系统释放。
5、 程序代码区。存放函数体的二进制代码。
9、 C++中,编译器会默认提供的构造函数有哪几种
答:只有一种,默认构造函数。(不带参数的构造函数)
10、 C++中默认构造函数有几种,详细描述每一种。
答:两种:1.不带有任何参数的构造函数。比如 Example();
如果用户没有定义任何构造函数,则编译器会默认提供这个构造函数。
2.带有默认值得构造函数。比如Example(int a = 23);
11、 有哪几种情况只能用intialization list (初始化列表)?
1、 引用成员变量的初始化
2、 const成员变量的初始化
3、 对象成员变量的初始化
4、 在继承关系中,基类部分成员的初始化
12、 解释此函数原型中三个const的作用const int* computeResult( const int& var ) const
答:第一个函数返回值的const表示返回的是一个常量指针;参数const说的是var是一个常引用,无法通过var来修改变量的值;第三个const表示computeResult函数是一个常成员函数,无法在函数中修改对象成员变量的值。
13、
char * const p;
char const * p
const char *p
上述三个有什么区别?
答:char *const p:定义了一个指针常量,指针指向不能发生变化,但是可以修改指向的变量的值。定义的时候必须要初始化。
char const *p: 定义了一个常量指针,指向常量的指针。无法修改指向的变量的内容,但是可以改变指针的指向。
const char *p: 定义了一个常量指针。指向常量的指针。无法修改指向的变量的内容,但是可以改变指针的指向。
14、请说出const与#define 相比,有何优点?
答:1.const在程序运行时,而#define是发生在预处理阶段。
2.工作原理不同:const用来修饰变量或者类型,可以进行类型检查。而#define只是简单的文本替换,没有安全的类型检查。
3.有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
15、 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
答:将引用作为函数返回值类型的格式为:int &fun();
好处:不会生成对象的副本,不会产生临时对象,直接返回的是返回的对象。
遵循的规则:不能返回局部变量或者局部对象的引用,因为局部变量或者局部对象返回之后会被释放掉。
16、 请说出static和const关键字尽可能多的作用
答:
static关键字的作用:
1. 隐藏。全局变量或者全局函数加上static则其他文件无法访问。
2. 保持变量内容的持久。由于static变量存储在全局/静态区,一经初始化则直到程序结束后才会被释放。
3. 默认初始化变量为0.经static修饰的变量都会被默认初始化为0
4. 在定义类的时候将成员变量或者成员函数加上static,则变为属于类的成员,所有对象共享。
const关键字的作用:
1. 修饰变量:const int a = 10;则a成为一个常量。
2. 修饰指针。分为常量指针和指针常量
3. 修饰引用。使引用成为常引用,无法通过引用修改变量的值
4. 修饰数组。数组成为常数组,无法修该里面元素的值,只能访问。
5. 修饰函数参数。不生成变量的副本,提高效率。
修饰类成员函数。使类的成员函数成为const成员函数,在函数中无法修改类的成员变量的值。
17、 “引用”与指针的区别是什么?
答:
相同点:
1.都是地址的概念;指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
1.指针是一个实体,而引用仅是个别名;
2.引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
3.引用没有const,指针有const,const的指针不可变;
4.引用不能为空,指针可以为空;
5.“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
6.指针和引用的自增(++)运算意义不一样;
7.引用是类型安全的,而指针不是 (引用比指针多了类型检查)
18、 在什么时候需要使用“常引用”?
答:如果既要利用引用提高效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
19、
下面使用常引用的例子合不合法
int a;
const int & ra = a;
ra = 1;
a = 1;
答:不合法。由于ra声明为常引用,故无法通过ra来修改变量a的值。
20、 将“引用”作为函数参数的类型有哪些特点?
答:1.传递引用给函数与传递指针的效果是一样的,这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
2.使用引用传递函数的参数,在内存中并没有产生实参的副本,它直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占的空间都好。
3.使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
21、 什么是“引用”?声明和使用“引用”要注意哪些问题?
答:引用就是对某个变量起一个别名。对引用的操作与对原变量的操作的效果是完全一样的。
声明一个引用的时候,需要对其进行初始化。声明一个引用,不是定义了一个变量,它只是目标变量名的一个别名。
22、 谈谈你对this指针的理解
答:this指针在产生对象的时候初始化,存储的是自身对象的地址。此外,this指针作为类成员函数的一个隐藏参数,通过this指针可以访问到类中的成员变量或者成员函数。
23、 静态成员变量是类的属性还是对象的属性,在内存中的存储情况是什么样的
答:类的属性。存储在全局/静态区
24、 static变量和static 函数各有什么特点?
答:1. 用于局部变量中,成为静态局部变量. 静态局部变量有两个用法,记忆功能和全局生存期.
2. 用于全局变量,主要作用是限制此全局变量被其他的文件调用.
3. 用于类中的成员.表示这个成员是属于这个类但是不属于类中任意特定对象
static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
25、 怎样把函数声明为类的友元函数,友元函数有什么作用
答:加一个friend关键字即可。友元函数可以访问类的私有成员
26、 输出操作符、下标操作符、赋值运算符、自增自减操作符,算术运算符以及关系运算符中哪些操作符只能用类的成员函数实现,那些只能用类的友元函数实现,哪些即可以用类的成员函数又可以用类的友元函数实现
答: 类的成员函数来实现:下标操作符、赋值运算符(=、[]、()、->)
类的友元函数来实现:输出操作符
类的成员函数和类的友元函数:自增自减操作符、算术运算符、关系运算符
27、 什么是深拷贝,什么是浅拷贝
答:深拷贝和浅拷贝都发生在拷贝对象的时候,并且当对象里面有指针成员并且指针成员指向堆上的空间才有这个区别。比如定义一个Example类:
Class Example
{
…
int *p;
};
Example a;
Example b = a; //这时候会调用拷贝构造函数,发生对象拷贝(深拷贝或者浅拷贝)
深拷贝:对象a里面的指针成员p指向堆上的一块空间,对象b里面的指针成员p指向堆上的另一块空间,但是它们堆空间里面的内容是一样的。(这是两块不同的堆空间)
浅拷贝:对象a里面的指针成员p指向堆上的一块空间,对象b里面的指针成员p也指向这块空间。(两个指针指向同一块堆上的空间)
简单的来说,深拷贝是指针成员指向不同的堆空间,浅拷贝是指针成员指向相同的堆空间。
28、 C++中什么时候会调用拷贝构造函数
答:1.用已经定义好的对象来初始化对象时
2.作为函数的参数的时候
3.作为函数返回值,从函数返回的时候
29、 写一个拷贝构造和赋值运算符重载函数的例子,并说明分别什么情况下会调用它们
答:定义一个Complex类,有两个私有成员(实部和虚部) r i;
Complex(constComplex& complex)
{
r = complex.r;
i = complex.i;
}
Complex&operator=(Complex &complex)
{
if (this == &complex)
return *this;
r = complex.r;
i =complex.i;
return *this;
}
拷贝构造函数:1.初始化对象2.作为函数参数3.从函数中返回
赋值运算符重载:赋值的时候
30、 阐述构造函数和析构函数的特点,另外构造函数和析构函数什么时候会被调用,两种函数的作用分别是什么
答:构造函数:
1. 函数名和类名一样。
2. 函数没有返回值类型
3. 函数一般声明为public
4. 函数可以带有参数
析构函数:
1. 函数名和类名一样,前面加上~.
2. 必须声明为public
3. 函数没有返回值
4. 函数不能带有参数,程序中只能有一个。
构造函数在对象产生的时候调用,析构函数在对象释放的时候调用。
构造函数主要用来做一些初始化的工作,比如初始化对象的数据成员,打开文件、数据库等。析构函数主要用来做一些对象的收尾工作,比如清理空间,关闭打开的文件、数据库等。
31、 C++中的空类,默认产生哪些类成员函数?
答:默认构造函数、析构函数、赋值运算符重载函数、拷贝构造函数
32、 C++继承是如何工作的?
答:派生类继承自基类,除了构造函数、赋制运算符重载、析构函数之外,基类的其他成员函数和成员变量都会被派生类继承。调用继承来的成员函数和父类调用一样。当然,派生类可以重写继承自基类的函数。
33、 简述public 、protected、 private三者的区别
答: private继承:基类中所有的成员都将无法访问。
protected继承:在派生类中,基类中的public成员和protected成员可以直接访问。在类外则都无法访问。
public继承:在派生类中,基类中的public成员和protected成员可以直接访问。在类外可以直接访问public成员。
34、 什么函数不能声明为虚函数
答:普通函数(非类成员函数);构造函数;静态成员函数;内联成员函数;友元函数
35、 函数重载与函数覆盖有什么不同,它们与多态有什么关系?
答:函数重载:函数名一样,参数的个数或者类型不一样。
函数覆盖:函数一模一样。(函数覆盖发生在继承重)
函数覆盖是多态实现的条件之一(多态:1.继承2.虚函数3.函数重写(覆盖)4.父类指针指向子类对象)。
36、 C++中的继承、虚函数、纯虚函数是什么?谈一下自己的理解。
答:C++中的继承说的是子类继承自父类的成员变量和成员函数,主要的好处是代码重用。
虚函数:在普通的成员函数添加virtual关键字,则成为虚函数。虚函数是多态实现的一部分。(多态包括:1.继承2.函数重写3.虚函数4.父类指针或者引用指向子类对象)
纯虚函数:将类声明为抽象类,表明这个类不能被实例化,不能产生对象。主要是作为接口来使用。
37、 基类的虚函数,被子类重写时还需要申明为virtual吗?为什么。
答:不需要了,子类会默认加上virtual
38、 构造函数可否是虚函数,为什么?析构函数呢,可否是纯虚的呢?
答:构造函数不能声明为虚函数,析构函数可以声明为虚函数,但是析构函数不能声明为纯虚函数。
1.每一个拥有虚成员函数的类都有一个指向虚函数表的指针。对象通过虚函数表里存储的虚函数地址来调用虚函数。
那虚函数表指针是什么时候初始化的呢?当然是构造函数。当我们通过new来创建一个对象的时候,第一步是申请需要的内存,第二步就是调用构造函数。试想,如果构造函数是虚函数,那必然需要通过vtbl来找到虚构造函数的入口地址,显然,我们申请的内存还没有做任何初始化,不可能有vtbl的。因此,构造函数不能是虚函数。
2.析构函数可以声明为虚函数。当基类指针指向派生类对象的时候,通过基类指针删除派生类对象,声明基类析构函数为虚函数,则会调用派生类的析构函数,这样能保证内存不发生泄露。
3.析构函数可以声明为纯虚函数,但是必须要给出定义。
39、 虚析构函数有什么作用?
答:如果构造函数打开了一个文件,最后不需要使用时文件就要被关闭。析构函数允许类自动完成类似清理工作,不必调用其他成员函数。
析构函数也是特殊的类成员函数。简单来说,析构函数与构造函数的作用正好相反,它用来完成对象被删除前的一些清理工作,也就是专门的扫尾工作。
40、 什么是动态绑定,动态绑定有什么好处
答:动态绑定是指在执行期间(非编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
好处:根据对象的实际类型来调用相应对象的函数。扩展性强
41、 什么是多态,多态的作用?
答:多态:对不同类的对象发出相同的消息将会有不同的行为,具体表现在程序中是父类指针指向子类对象,调用实际子类对象的函数。
作用:
1. 应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承
2. 派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。 //多态的真正作用,以前需要用switch实现
42、 “引用”与多态的关系?
答:多态的实现条件有:1.要有继承 2.要有虚函数 3.要有函数重写 4.父类指针或者父类引用指向子类对象。
由此可见,引用在多态中也有着其作用。
43、 什么是抽象基类,抽象基类可不可以被实例化,为什么
答:类中至少含有一个纯虚函数的类被称之为抽象类。
抽象类不能被实例化,无法产生对象。
抽象类被当作接口来使用,纯虚函数只有声明没有定义,也是无法产生对象的原因。
44、 为什么要引入抽象基类和纯虚函数?
答:主要目的是为了实现一种接口的效果。(比如动物类这样适合产生对象,只适合作为接口出现的类)
45、多重继承的内存分配问题:
比如有class A : public class B, public classC {} 那么A的内存结构大致是怎么样的?
答:A的内存大概分为三部分。第一部分为继承自基类B的成员,第二部分为继承自基类A的成员,第三部分为自己类中定义的成员。
46、 多重继承如何消除向上继承的二义性。
答:采用虚基类
47、 简单描述多重继承中怎样产生的菱形继承问题,又是如何解决的呢
答:A类和B类共同继承自C类,D类分别继承自A类和B类,那么D类中将会有“两个C类”,一个来自A类,一个来自B类。
采用虚基类来解决
48、简述map和 multimap的区别,那list 和 vector的区别呢
答:map是单纯的一对一映射,key不允许重复;multimap可以是一对多映射,key允许重复;
list容器顺序访问每个元素,可以在任何位置添加或者删除元素;vector容器是随机访问元素,一般在末尾添加、删除元素。
49、
说出以下三种函数中分别可以抛出什么异常
void f();
void f() throw()
int func( int x ) throw( int,Error_message)
答:实际都可以抛出任意类型的异常,只不过为了增强程序的可读性,抛出异常的类型通常自己在函数头后面指定。
以上从前往后依次为:
可以抛出任何类型异常
不能抛出任何异常
只能抛出int类型或者Error_message类型异常
50、 进程间通信的方式有?
答:进程间通信主要包括管道, 系统IPC(包括消息队列,信号量,共享存储), SOCKET.
具体请看:http://blog.csdn.net/yufaw/article/details/7409596
51、
分析下面程序的运行结果
#include <iostream.h>
class B
{
public:
B(){}
B(inti){b=i;}
virtualvoid virfun()
{
cout<<"B::virfun()called.\n";
}
private:
intb;
};
classD:public B
{
public:
D(){}
D(inti,int j):B(i){d=j;}
private:
intd;
voidvirfun()
{
cout<<"D::virfun()called.\n";
}
};
voidfun(B *obj)
{
obj->virfun();
}
intmain()
{
D*pd=new D;
fun(pd);
return0;
}
答:满足多态实现的条件,故:
D::virfun() called.
52、
写出运行结果:
{// test1
char str[] = "world"; cout << sizeof(str) << ": ";
char *p = str; cout << sizeof(p) << ": ";
char i = 10; cout << sizeof(i) << ": ";
void *pp = malloc(10); cout << sizeof(pp) << endl;
}
答:32位机器下:
6 4 1 4
64位机器下:
6 8 1 8
53、
分析一下这段程序的输出
class B
{
public:
B()
{
cout<<"defaultconstructor"<<endl;
}
~B()
{
cout<<"destructed"<<endl;
}
B(int i):data(i) {
cout<<"constructedby parameter "<< data <<endl;
}
private:
int data;
};
B Play( B b)
{
return b ;
}
int main()
{
Bb;
Play(b)
return0;
}
输出结果为:
defaultconstructor"
destructed
destructed
destructed
54、
运行以下程序会有什么样的结果:
int main()
{
char *str = new char[100];
strcpy( str, "hello" );
delete []str;
if(str != NULL)
{
strcpy(str,”world”);
printf(“%s\n”,str);
}
return 0;
}
答:此题需要注意,free掉堆空间后,str指针保存的地址值没有变,依然保存着堆空间的地址。所以,str不为NULL。此时str成为一个野指针,对野指针进行strcpy操作,程序会崩溃。
55、
运行以下程序会有什么样的结果:
void GetMemory( char **p, int num )
{
*p = new char[num];
}
int main( )
{
char *str = NULL;
GetMemory( &str, 100 );
strcpy( str, "hello" );
cout<<str<<endl;
return 0;
}
答:程序输出hello,但是发生内存泄露
56、
运行以下程序会有什么样的结果:
char *GetMemory( void )
{
char p[] ="hello world";
return p;
}
int main( )
{
char *str =NULL;
str =GetMemory();
cout<<str<<endl;
return 0;
}
答:需要注意,GetMemory函数返回的是局部数组的地址。之后str指向被释放了的局部数组的空间。然后打印str,此时str指向空间的内容不可知,有可能是hello world,也有可能是乱码。
57、
运行以下程序会有什么样的结果:
void GetMemory( char *p )
{
p = new char[100];
}
int main( )
{
char *str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
cout<<str<<endl;
return 0;
}
答:
GetMemory函数将str赋值给p,之后p指针指向堆上的空间。注意,函数传递参数是单向的,也就是说只能由实参传递给形参,之后形参、实参再无关系。故str的值依然为NULL。为空指针调用strcpy函数,程序崩溃。
58、
分析test3函数指出其中问题:
void test3(char* str1)
{
char string[10];
if( strlen( str1 ) <= 10 )
{
strcpy( string, str1 );
}
}
答:
由于数组string共有10个空间,而strlen函数返回的值不包括\0,故有可能会溢出。应该改成if( strlen( str1 ) < 10 )
59、
对以下程序进行分析:
int main()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1[i] = 'a';
}
strcpy( string, str1 );
return 0;
}
答:数组str1共有10个元素,通过for循环将所有的元素都设置为’a’字符。调用strcpy函数将str1里面的内容拷贝到string数组中。由于在数组str1中不存在\0,故拷贝的时候,会发生溢出。
60、
找错题
运行以下程序会有什么结果:
int main()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
return 0;
}
数组string共有10个元素,而str1指向的字符串共有11个字符(末尾的\0),故执行strcpy的时候会发生溢出。
61、
输出下面程序结果。
#include <iostream.h>
class A
{
public:
virtual void print(void)
{
cout<<"A::print()"<<endl;
}
};
class B:public A
{
public:
virtual void print(void)
{
cout<<"B::print()"<<endl;
};
};
class C:public B
{
public:
virtual void print(void)
{
cout<<"C::print()"<<endl;
}
};
void print(A a)
{
a.print();
}
intmain()
{
A a, *pa,*pb,*pc;
B b;
C c;
pa=&a;
pb=&b;
pc=&c;
a.print();
b.print();
c.print();
pa->print();
pb->print();
pc->print();
print(a);
print(b);
print(c);
return0;
}
答:
A::print()
B::print();
C::print();
A::print()
B::print();
C::print();
A::print()
A::print()
A::print()
62、
下面哪个类中的哪几个函数是虚函数
class A
{
virtual void func1();
void func2();
}
Class B: class A
{
void func1(){cout << "fun1 in class B" << endl;}
virtual void func2(){cout << "fun2 in class B" << endl;}
}
答:
类A中的func1函数
类B中的fun1函数、func2函数
63、
unsigned short array[]={1,2,3,4,5,6,7};
int i = 3;
*(array + i) = ?
答:4
64、
i最后等于多少?
int i = 1;
int j = i++;
if((i>j++) && (i++ == j)) i+=j;
答:5
65、
下面的代码有什么问题?
class A
{
public:
A() { p=this; }
~A() { if(p!=NULL) { delete p; p=NULL; } }
A* p;
};
答:构造函数中this指针代表的是自身对象的地址,故不为空。析构函数中判断p是否为空,然后删除p指向的空间,将p置为NULL;
如果申请的对象在栈上,则程序出错。
66、
不用库函数自己实现strlen函数
答:
int strlen(char *str)
{
int count = 0;
if (str ==NULL)
{
throw"str is NULL";
}
while (str++!= '\0')
{
count++;
}
return count;
}
67、
已知strcpy的函数原型:char *strcpy(char *strDest, const char *strSrc)其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数 strcpy。
答: char*strcpy(char *strDest, const char *strSrc)
{
char *str =strDest;
if (strDest ==NULL || strSrc == NULL)
{
throw"NULL str";
}
while(*strDest++ = *strSrc++ != '\0')
return str;
}
68、
定义一个字符栈类Stack(包括类的实现)。数据成员包括一个存放字符的数组stck[ ]和一个栈指针tos。栈数组的尺寸由常量SIZE确定。栈的基本操作为Push()和Pop()。
答:
#include <iostream>
using namespace std;
const int SIZE = 100;
class Stack
{
friendostream& operator<<(ostream& out, const Stack &stack);
public:
Stack();
int isEmpty();
int isFull();
int push(char*str);
int pop(char**str);
private:
char*stack[SIZE];
char tops; //栈顶指针
};
Stack::Stack()
{
tops = -1;
}
int Stack::isEmpty()
{
if (tops ==-1)
return 1;
else
return 0;
}
int Stack::isFull()
{
if(tops == SIZE- 1)
return 1;
else
return 0;
}
int Stack::push(char *str)
{
if (isFull())
{
return 0;
}
stack[++tops]= str;
return 1;
}
int Stack::pop(char **str)
{
if (isEmpty())
{
return 0;
}
*str =stack[tops--];
return 1;
}
ostream& operator<<(ostream &out, constStack &stack)
{
for (int i =0; i <= stack.tops; i++)
{
out<< stack.stack[i] << " ";
}
return out;
}
int main()
{
Stack s;
char *str;
s.push("one");
s.push("two");
s.push("three");
s.push("four");
s.push("five");
cout <<s << endl;
s.pop(&str);
cout << str << endl;
cout <<s << endl;
return 0;
}
69、
已知String类声明如下,对其进行实现:
classString
{
public:
String(const char*str = NULL); // 通用构造函数
String(const String&another); // 拷贝构造函数
~String(); // 析构函数
String& operater=(const String &rhs); // 赋值函数
private:
char* m_data; // 用于保存字符串
};
尝试写出类的成员函数实现。
答: classString
{
public:
String(const char*str = NULL); // 通用构造函数
String(const String &another); // 拷贝构造函数
~String(); // 析构函数
String& operater =(const String &rhs); // 赋值函数
private:
char* m_data; // 用于保存字符串
};
String::String(const char *str)
{
if (str ==NULL)
{
m_data =(char *)malloc(1);
*m_data ='\0';
}
else
{
m_data =(char *)malloc(strlen(str) + 1);
strcpy(m_data,str);
}
}
String::String(const String &another)
{
m_data = (char*)malloc(strlen(another.m_data));
strcpy(m_data,another.m_data);
}
String::~String()
{
delete []m_data;
}
String& String::operater=(const String &rhs)
{
if(this !=&rhs)
{
delete []m_data;
m_data = (char *)malloc(strlen(rhs.m_data)+ 1);
strcpy(rhs.m_data,m_data);
}
return *this;
}
70、
实现冒泡排序
答:
void buddleSort(int *array, int length)
{
for(int i = 0;i < length - 1; i++)
for (intj = 0; j < length - i - 1; j++)
{
if(array[j]> array[j+1])
{
inttemp = array[j];
array[j]= array[j+1];
array[j+1]= temp;
}
}
}
71、
谈谈你对冒泡排序、插入排序、选择排序和快速排序的理解
答:冒泡排序:将序列划分为无序和有序区,不断通过交换较大元素至无序区尾完成排序。
插入排序:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中区,最终将所有无序区元素都移动到有序区完成排序。
选择排序:将序列划分为无序和有序区,寻找无序区中的最小值和无序区的首元素交换,有序区扩大一个,循环最终完成全部排序。
快速排序:不断寻找一个序列的中点,然后对中点左右的序列递归的进行排序,直到全部序列排序完成。
72、
已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序。(保留所有结点,即便大小相同)
答:
struct Node *mergeLink(struct Node *head1, struct Node*head2)
{
struct Node*head = (struct Node *)malloc(sizeof(struct Node));
head->next= NULL;
head->data= 0;
struct Node *p= head;
if (head1 ==NULL || head2 == NULL)
{
free(head);
throw"link is NULL";
}
while (head1!= NULL || head2 != NULL)
{
if (head1!= NULL && head1->data < head2->data)
{
p->next=head1;
head1= head1->next;
}
else if(head2 != NULL )
{
p->next= head2;
head2= head2->next;
}
p =p->next;
}
p =head->next;
free(head);
return p;
}
73、
链表题:一个链表的结点结构
typedef struct Node
{
int data ;
struct Node *next ;
}NODE;
已知链表的头结点head,写一个函数把这个链表逆序
答:
Node*reverse(Node *head) //链表逆序
{
Node *p = head;
Node *q = head;
Node *m = head;
if(head == NULL)
{
printf("逆序链表:这是一个空链表\n");
return NULL;
}
q = p->next;
while(q != NULL) //保持位置p、q、m
{
m = q->next;
q->next = p;
p = q;
q = m;
}
head->next = NULL;
return p;
}
74、线程与进程的区别和联系?
答:
定义:
一、进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。
二、线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以创建和撤销另一个线程;
进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
(4)处理机分给线程,即真正在处理机上运行的是线程。
(5)线程是指进程内的一个执行单元,也是进程内的可调度实体。
线程与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
(4)系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些。
线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序运行效率。
线程在执行过程中,每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,有应用程序提供多个线程执行控制。
从逻辑角度看,多线程的意义子啊与一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
75、分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)
答:
BOOL: if(var)
int: if(var == 0)
float: if(var <=0.0001 && var >= -0.0001)
由于float类型在内存中存储并不是精确存储的,故不能直接和0进行比较。
76、重载(overload)和重写(overried“覆盖”)的区别?
答:
重载:函数名一样,参数类型和个数不一样。也就是拥有相同名字的不同函数。
重写:函数定义一模一样,多发生在继承中。子类将继承来的父类函数给重新定义,将父类函数给屏蔽掉。
77、c++11增加了哪些新特性?请列举至少6条
auto
在C++11之前,auto关键字用来指定存储期。在新标准中,它的功能变为类型推断。auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。各种作用域内声明变量都可以用到它。
nullptr
以前都是用0来表示空指针的,但由于0可以被隐式类型转换为整形,这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值,用来指代空指针。nullptr和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为bool型(取值为false)。但是不存在到整形的隐式类型转换。
Range-based for loops (基于范围的for循环)
为了在遍历容器时支持”foreach”用法,C++11扩展了for语句的语法。用这个新的写法,可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。如果你只是想对集合或数组的每个元素做一些操作,而不关心下标、迭代器位置或者元素个数,那么这种foreach的for循环将会非常有用。
新标准加入了两个新的标识符(不是关键字)::
override,表示函数应当重写基类中的虚函数。
final,表示派生类不应当重写这个虚函数。
Strongly-typed enums 强类型枚举
传统的C++枚举类型存在一些缺陷:它们会将枚举常量暴露在外层作用域中(这可能导致名字冲突,如果同一个作用域中存在两个不同的枚举类型,但是具有相同的枚举常量就会冲突),而且它们会被隐式转换为整形,无法拥有特定的用户定义类型。
在C++11中通过引入了一个称为强类型枚举的新类型,修正了这种情况。强类型枚举由关键字enum class标识。它不会将枚举常量暴露到外层作用域中,也不会隐式转换为整形,并且拥有用户指定的特定类型(传统枚举也增加了这个性质)。
Smart Pointers 智能指针
Lambdas:匿名函数(也叫lambda)已经加入到C++中,并很快异军突起。这个从函数式编程中借来的强大特性,使很多其他特性以及类库得以实现。你可以在任何使用函数对象或者函子(functor)或std::function的地方使用lambda。
非成员begin()和end():他们是新加入标准库的,除了能提高了代码一致性,还有助于更多地使用泛型编程。它们和所有的STL容器兼容。更重要的是,他们是可重载的。所以它们可以被扩展到支持任何类型。对C类型数组的重载已经包含在标准库中了。
static_assert和 type traits
static_assert提供一个编译时的断言检查。如果断言为真,什么也不会发生。如果断言为假,编译器会打印一个特殊的错误信息。
Move semantics (Move语义):这是C++11中所涵盖的另一个重要话题。C++11加入了右值引用(rvalue reference)的概念(用&&标识),用来区分对左值和右值的引用。左值就是一个有名字的对象,而右值则是一个无名对象(临时对象)。move语义允许修改右值(以前右值被看作是不可修改的,等同于const T&类型)。
78、什么是多线程?
是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
79、举例说明对象和类
类:电脑
对象:苹果电脑,联想电脑,华硕电脑
对象的属性:品牌,制造商,内存大小,磁盘大小等
对象的行为:上网,看视频,发邮件等
80、什么时候需要实现深拷贝?
当类中有只想堆空间的指针成员时需要实现深拷贝
81、类中包含几种成员,分别是什么?
类中有两种成员:成员变量以及成员函数。其中成员变量分为静态的成员变量以及非静态的成员变量;成员函数分为静态的成员函数以及非静态的成员函数。
82、什么是默认构造函数?
不传递任何参数就可以被调用的构造函数。默认构造函数分为两种:本身不带参数以及带参数,但每一个参数都带默认值。
83、继承的作用?
1、表示现实中两个对象之间的is-a关系
2、实现代码复用
84、继承方式对派生类中成员的影响?
如果继承方式为private,则基类中成员继承到派生类中权限均为private;
如果继承方式为protected,则成员继承到派生类中权限不高于protected(即public->protected,其它的不变)
如果继承方式为public,则所有成员的权限保持不变
- C/C++面试题二
- C常见面试题<二>
- [C#]c#面试笔试题(二)
- C语言面试题精粹(二)
- c#面试笔试题(二)
- 经典C/C++面试题(二)
- C语言面试题总汇(二)
- C/C++面试题(二)
- 经典C/C++面试题(二)
- C语言面试题(二)
- linux C程序员经典面试题二
- c语言常见面试题(二)
- linux C程序员经典面试题二
- 嵌入式C语言面试题(二)
- 嵌入式C语言面试题(二)
- C语言面试题(二)
- C/C++面试题(二)
- 华为经典C语言面试题(二)
- u-boot中SPL源代码分析
- C++面试题(一)
- Ajax获取html数据传到后台,获取返回值,添加到input框里
- Android BaseActivity 分享
- C++相对路径设置
- C++面试题(二)
- Retrofit2.0的简单使用
- (二)洞悉linux下的Netfilter&iptables:内核中的ip_tables小觑
- ubuntu16.04 Error: libcudart.so.7.5: cannot open shared object file: No such file or directory最新解决方案
- 二分法查找详细讲解
- Android列表侧滑删除就是这么简单
- 代码中高度、宽度的单位
- Java的IO流_每天小小的改变,可以改变世界!
- springboot activiti工作流简单示例