C和C++面试秘籍

C++一些常见问题

1. C和C++的区别是什么?

1,C是面向过程的语言,C++是面向对象的编程语言。面向对象是一种对现实世界理解和抽象的方法。

2,C中函数不能进行重载,C++中函数可以重载。

3,C++在C的基础上增添类。

4,C中struct和C++的类(class),除了默认访问权限外(struct的成员默认访问修饰符是public,而class默认的是private;),别的功能几乎相同。

5,C和C++动态管理管理内存的方法不同,C是使用malloc/free函数,而C++除此之外还有new/delete关键字。

6,C++中有引用而C没有。

2. 关键字static、const、extern作用

static关键字的作用: 1)隐藏,当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。

​ 2)保持变量内容的持久。

​ 3)默认初始化为0

​ 4)C++中的类成员声明static

const关键字的作用:

(1)阻止一个变量被改变

(2)声明常量指针和指针常量

(3)const修饰形参,表明该输入参数在函数内部不能改变其值

(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量(const成员一般在成员初始化列表处初始化)

(5)对于类的成员函数,有时候必须指定其返回值为const类型

extern关键字的作用:

(1)extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

(2)extern "C"的作用是让 C++ 编译器将extern "C"声明的代码当作 C 语言代码处理,可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接。

3. 内存有哪几种类型及分配方式

类型:

​ 1,栈区(stack):如局部变量,函数参数

​ 2,堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。

​ 3,全局/静态区:如全局变量、static变量

​ 4,常量存储区:存放常量字符串

​ 5,程序代码区:存放函数体的二进制代码

​ 6,c++中还有所谓的自由存储区(new)

分配方式:

​ 1,从静态存储区域分配

​ 2,在栈上分配

​ 3,在堆上分配,也被称为动态内存分配

4. 堆和栈的区别?

1)堆存放动态分配的对象——即那些在程序运行时动态分配的对象,比如 new 出来的对象,其生存期由程序控制;

2)栈用来保存定义在函数内的非static对象,如局部变量,仅在其定义的程序块运行时才存在;

3)静态内存用来保存static对象,类static数据成员以及定义在任何函数外部的变量,static对象在使用之前分配,程序结束时销毁;

4)栈和静态内存的对象由编译器自动创建和销毁。

###5. 什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

方法:

1). 使用的时候要记得指针的长度.

2). malloc的时候得确定在那里free.

3). 对指针赋值的时候应该注意被赋值指针需要不需要释放.

4). 动态分配内存的指针最好不要再次赋值.

5). 在C++中应该优先考虑使用智能指针.

6. 引用和指针的区别?

  1. 指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。

  2. 引用在定义时必须初始化,并且不能够改变。(引用的值不能为NULL)指针在定义时不一定需要初始化,并且指向的空间可变。

  3. 有多级指针,但是没有多级引用,只能有一级引用。

  4. 指针和引用的自增运算结果不同。(指针是指向下一个空间,引用是引用的变量加1)

  5. sizeof引用得到的是所指向的变量的大小,sizeof指针得到的是指针的大小。

  6. 引用访问一个变量是直接访问,而指针访问是间接访问。

  7. 作为参数时也不一样,传指针本质上是传值,传递的值是指针的地址;传引用本质是传地址,传递的是变量的地址。

7. 面向对象的三大特性

  1. 抽象

  2. 继承

  3. 多态

8. 形参和实参的区别?

1)形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。

2)实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。

3)函数调用中发生的数据传送是单向的。只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。

9. vitrual 关键字

(1) 静态函数可以声明为虚函数吗?

原因主要有两方面:

静态函数不可以声明为虚函数,同时也不能被const 和 volatile关键字修饰

static成员函数不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义

虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,静态成员函数没有this指针,所以无法访问vptr。

(2)构造函数可以为虚函数吗?

构造函数不可以声明为虚函数。同时除了inline|explicit之外,构造函数不允许使用其它任何关键字。

为什么构造函数不可以为虚函数?

尽管虚函数表vtable是在编译阶段就已经建立的,但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数,编译器会在构造函数中添加代码来创建vptr。 问题来了,如果构造函数是虚的,那么它需要vptr来访问vtable,可这个时候vptr还没产生。 因此,构造函数不可以为虚函数。

我们之所以使用虚函数,是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的,实例的类型必须是明确的。 因此,构造函数没有必要被声明为虚函数。

(3)析构函数可以为虚函数吗?

析构函数可以声明为虚函数。如果我们需要删除一个指向派生类的基类指针时,应该把析构函数声明为虚函数。 事实上,只要一个类有可能会被其它类所继承, 就应该声明虚析构函数(哪怕该析构函数不执行任何操作)。

(4)虚函数可以为私有函数吗?

  • 基类指针指向继承类对象,则调用继承类对象的函数;
  • int main()必须声明为Base类的友元,否则编译失败。 编译器报错: ptr无法访问私有函数。 当然,把基类声明为public, 继承类为private,该问题就不存在了。

5)虚函数可以被内联吗?

通常类成员函数都会被编译器考虑是否进行内联。 但通过基类指针或者引用调用的虚函数必定不能被内联。 当然,实体对象调用虚函数或者静态调用时可以被内联,虚析构函数的静态调用也一定会被内联展开。

  • 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
  • 内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。
  • inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 Base::who()),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。

volatile常见问题

(1)一个参数既可以是const还可以是volatile吗?为什么?

​ 可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

(2)一个指针可以是volatile吗?为什么? 可以。尽管这并不常见。一个例子是当一个中断服务子程序修该一个指向一个buffer的指针时。(3)下面的函数有什么错误?

1
2
3
4
int square(volatile int *ptr) 
{
return *ptr * *ptr;
}

这段代码有点变态,其目的是用来返回指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码

1
2
3
4
5
6
7
int square(volatile int *ptr) 
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由于*ptr的值可能被意想不到地改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:

1
2
3
4
5
long square(volatile int *ptr) 
{
int a=*ptr;
return a * a;
}
  • volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。
  • volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)
  • const 可以是 volatile (如只读的状态寄存器)
  • 指针可以是 volatile

C和C++中的Struct区别

C C++
不能将函数放在结构体声明 能将函数放在结构体声明
在C结构体声明中不能使用C++访问修饰符。 public、protected、private 在C++中可以使用。
在C中定义结构体变量,如果使用了下面定义必须加struct。 可以不加struct
结构体不能继承(没有这一概念)。 可以继承
若结构体的名字与函数名相同,可以正常运行且正常的调用! 若结构体的名字与函数名相同,使用结构体,只能使用带struct定义!

struct与class区别

区别:

最本质的一个区别就是默认的访问控制

默认的继承访问权限。struct 是 public 的,class 是 private 的。

struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。

友元函数与友元类

  • 能访问私有成员
  • 破坏封装性
  • 友元关系不可传递
  • 友元关系的单向性
  • 友元声明的形式及数量不受限制