【面试题】C++综合面试题

来源:互联网 发布:g92车锥螺纹编程实例 编辑:程序博客网 时间:2024/06/05 21:16

1,简述C++虚函数作用及底层实现原理

虚函数的作用是使基类指针指向派生类对象时,访问派生类的同名函数,实现动态联编。
原理:每个含有虚函数的派生类对象都有一个或多个(多继承)虚函数表指针,虚函数表指针指向了一个虚函数表,如果该类重写了基类的虚函数,则存放派生类的虚函数地址,如果没有重写,则存放基类的虚函数地址。

2,一个对象访问普通函数和虚函数哪个更快?

答案是不一定。这就要看这个函数是否构成多态了,若是构成多态,那在访问这个函数的时候还需要去虚函数表中找,这时就是访问普通函数更快了,若不构成多态,则访问虚函数更快。

3,在什么情况下,析构函数需要是虚函数?

这道题上一篇博客中有具体讲解,链接戳这里。
http://blog.csdn.net/chaseraod/article/details/75195352

4,内联函数、构造函数、静态成员函数可以是虚函数吗?

首先要知道,虚函数是针对对象而言,在运行时才进行动态联编的。
(1)内联函数不可以是虚函数,因为内联函数是在函数体中进行替换,没有自己的地址,虚表指针无法找到它。
(2)构造函数不 能是虚函数,因为调用虚函数需要虚函数表指针,而在执行构造函数之前是没有虚函数表指针的。
(3)静态成员函数不可以是虚函数,静态函数是属于类本身的,不属于对象本身,自然无法有自己的虚函数表指针。

5,构造函数中可以调用虚函数吗?

答案是可以的。只是可能和你预期想要的结果不同。
你认为以下代码会输出什么?

class Base{public:    Base()    {        Fuction();    }    virtual void Fuction()    {        cout << "Base::Fuction" << endl;    }};class A : public Base{public:    A()    {        Fuction();    }    virtual void Fuction()    {        cout << "A::Fuction" << endl;    }};int main(){    A a;    system("pause");    return 0;}

来看运行结果
这里写图片描述

我们知道,函数的执行顺序是先调用基类的构造函数,再调用子类的构造函数。C++中规定:调用应用程序是正在构建或销毁的对象,所调用的函数是
构造函数或析构函数自身的类或其基类中的一个基类,但不是派生类中覆盖它的函数。

6,简述C++中虚继承的作用及底层实现原理?

作用:虚继承用于解决菱形继承的二义性和数据冗余问题。
底层实现原理:与编译器相关,一般通过虚基类 指针实现,即各对象中只保存一份父类的对象,多继承时通过虚基类指针引用该公共对象,从而避 免菱形继承中的二义性问题。

7,引用和指针有什么区别?

(1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:
int a=1;int *p=&a;
int a=1;int &b=a;
上面定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址。
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。
(2)可以有const指针,但是没有const引用;
(3)指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
(4)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
(5)指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
(6)”sizeof引用”得到的是所指向的变量(对象)的大小,而”sizeof指针”得到的是指针本身的大小;
(7)指针和引用的自增(++)运算意义不一样;
(8)指针传参时形参是实参的一份临时拷贝,而引用传参改变的是实参本身,因此,引用传参不仅节约时间还节约空间。

8,const和define有什么区别?

(1) 编译器处理方式不同
  define宏是在预处理阶段展开。
  const常量是编译运行阶段使用。
(2) 类型和安全检查不同
  define宏没有类型,不做任何类型检查,仅仅是展开。
  const常量有具体的类型,在编译阶段会执行类型检查。
(3) 存储方式不同
  define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。)const常量会在内存中分配(可以是堆中也可以是栈中)。
(4)const 可以节省空间,避免不必要的内存分配。
(5) 提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
(6) 宏替换只作替换,不做计算,不做表达式求解; 宏预编译时就替换了,程序运行时,并不分配内存。

9,枚举类型优缺点?

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数

10,static关键字的作用有哪些?

C语言中:
1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。

2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。注意,只有在定义了变量后才能使用。如果变量定义在使用之后,要用extern 声明。所以,一般全部变量都会在文件的最开始处定义。

3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

linux中:
(1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

(2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

(3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

基于以上,C++对static做了扩展:
(4)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;

(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量

11,C++中const关键字的作用?

Const的使用
1、定义常量
(1)const修饰变量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的。
TYPE const ValueName = value;
const TYPE ValueName = value;

(2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义.

   extend const int ValueName = value; 

2、指针使用CONST
(1)指针本身是常量不可变
(char*) const pContent;
const (char*) pContent;

(2)指针所指向的内容是常量不可变
const (char) *pContent;
(char) const *pContent;

(3)两者都不可变
const char* const pContent;

(4)还有其中区别方法,沿着*号划一条线:
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

3、函数中使用CONST

(1)const修饰函数参数
a.传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)

void function(const int Var);

b.参数指针所指内容为常量不可变

void function(const char* Var);

c.参数指针本身为常量不可变(也无意义,因为char* Var也是形参)

void function(char* const Var);

d.参数为引用,为了增加效率同时防止修改。修饰引用参数时:

void function(const Class& Var); //引用参数在函数内不可以改变

void function(const TYPE& Var); //引用参数在函数内为常量不可变

这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,
然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性,
且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.

(2)const 修饰函数返回值
const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。
a.const int fun1() //这个其实无意义,因为参数返回本身就是赋值。
b. const int * fun2() //调用时 const int *pValue = fun2();
//我们可以把fun2()看作成一个变量,即指针内容不可变。
c.int* const fun3() //调用时 int * const pValue = fun2();
//我们可以把fun2()看作成一个变量,即指针本身不可变。

一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const
A test = A 实例)或某个对象的引用为const(const A& test = A实例)
,则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

4、类相关CONST

(1)const修饰成员变量
const修饰类的成员函数,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。
class A
{

const int nValue; //成员常量不能被修改

A(int x): nValue(x) { } ; //只能在初始化列表中赋值
}

(2)const修饰成员函数
const修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰。
class A
{

void function()const; //常成员函数, 它不改变对象的成员变量.

//也不能调用类中任何非const成员函数。
}

对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。

a. const成员函数不被允许修改它所在对象的任何一个数据成员。

b. const成员函数能够访问对象的const成员,而其他成员函数不可以。

(3)const修饰类对象/对象指针/对象引用
const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。

const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
例如:
class AAA
{
void func1();
void func2() const;
}
const AAA aObj;
aObj.func1(); ×
aObj.func2(); 正确

const AAA* aObj = new AAA();
aObj-> func1(); ×
aObj-> func2(); 正确

12,C++中包含哪几种强制类型转换,他们有什么区别和联系?

1.static_cast
最常用的类型转换符,在正常情况下的类型转换,如把int i 转换为 float f;
f = float(i); 或者 f =static_cast(i);

2.const_cast
用于去除const属性,把const类型的指针变为非const类型的指针,如const int* fun(int x, int
y){}
int* ptr = const_cast

class C{//.....C没有虚函数}class T{//...}int main(){dynamin_cast<T*>(new C);//错误}

修改后:

      class C         {         virtual void m();//C现在是多态         }         class T{         //...         }         int main()         {         dynamin_cast<T*>(new C);//正确       }

4.reinterpret_cast
reinterpret为重新解释的意思,即为数据的二进制形式重新解释,但是不改变其值,如:

inti;       char* ptr = "hello freind!";      i = reinterpret_cast<int>(ptr);