首页 > 编程语言 > 详细

C++面试题

时间:2019-06-19 23:58:41      阅读:771      评论:0      收藏:0      [点我收藏+]

指针和引用的区别:

引用不是一个对象,所以它没有实际的地址。指针是一个对象,它有实际的地址,所以我们可以定义指向指针的指针,但是绝对不能定义指向引用的指针。

引用在其生命周期中不能改变所绑定的对象,但是指针可以在其生命周期内先后指向不同的对象。

引用在定义的时候必须进行初始化,而指针可以不必进行初始化。

堆和栈的区别:

管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;

空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。

生长方向不同。堆向高地址生长,栈向低地址生长。

技术分享图片

分配方式不同。堆都是动态分配的,栈有2种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca函数进行分配。

new和delete是如何实现的,new 与 malloc的异同处:

delete会调用对象的析构函数,和new对应。free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

三种继承方式:

(1) 公有继承(public)   
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。

(2) 私有继承(private)  ------------------默认的继承方式(如果缺省,默认为private继承)

私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

(3) 保护继承(protected)   
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

class的区别:

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

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

define 和const的区别(编译阶段、安全性、内存占用等):

编译阶段:define 是预编译阶段展开,而const是在运行阶段使用

安全性:const常量是有数据类型的,那么编译器会对const变量的类型等安全性进行检查,但是define只是在预编译阶段展开,不会进行类型的安全检查,替换时可能产生安全错误。

内存占用:define不会占用内存,单纯的替换而已,const会占用内存,会有对应的内存地址。
# define pi  3.14+3.14 在a=pi*pi;时----->a=3.14+3.14*3.14+3.14;

const float pi = 3.14+3.14;实际为pi分配了内存,a=pi*pi;------------->a=(3.14+3.14)*(3.14+3.14);

 在C++中const和static的用法(定义,用途):

定义:
在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。

static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate=2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static。

用途
cosnt成员函数主要目的是防止成员函数修改对象的内容。即const成员函数不能修改成员变量的值,但可以访问成员变量。当方法成员函数时,该函数只能是const成员函数。

static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数没有this指针,这导致:1、不能直接存取类的非静态成员变量,调用非静态成员函数2、不能被声明为virtual.
const和static在类中使用的注意事项(定义、初始化和使用):

定义:
const可以在类内部定义,但是定义的位置不能初始化;static只能在类内部声明,定义只能在类的外面,并且定义的时候不能加static关键字

初始化:
const不能再定义的位置初始化,只能在类的构造函数的初始化列表中初始化;static初始化不能再类的内部进行初始化,必须在外部定义的时候初始化。

使用:
const的使用主要目的是防止成员函数修改对象的内容,即const成员函数不能修改成员变量的值,但可以访问成员变量。
static的使用目的是作为类作用域的全局函数。不能访问类的非静态数据成员,类的静态成员函数没有this指针,这导致不能直接存取类的非静态成员变量,调用非静态成员函数,不能声明为virtual.
C++中的const类成员函数(用法和意义),以及和非const成员函数的区别:

1.void fun() const;
表明是常量成员函数,这个const表明了该函数不会改变任何成员数据的值
2.void fun(const a) const;
表明是参数是常量的常量成员函数,接收的参数是常量,同时不能改变成员数据的值。

意义:为什么要这么做?
这是为了保证它能被const常量对象调用,我们都知道,在定义一个对象或者一个变量时,如果在类型前加一个const,如const int x;,则表示定义的量为一个常量,它的值不能被修改。但是创建的对象却可以调用成员函数,调用的成员函数很有可能改变对象的值。所以这个时候const类成员函数就出现了。

于是,我们把那些肯定不会修改对象的各个属性值的成员函数加上const说明符,这样,在编译时,编译器将对这些const成员函数进行检查,如果确实没有修改对象值的行为,则检验通过。以后,如果一个const常对象调用这些const成员函数的时候,编译器将会允许。
C++的顶层const和底层const:

指针如果添加const修饰符时有两种情况:
1 指向常量的指针(底层const):代表不能改变其指向内容的指针。声明时const可以放在类型名前后都可,拿int类型来说,声明时:const int和int const 是等价的。声明指向常量的指针也就是底层const,下面举一个例子:

int num_a = 1; 

int const *p_a = &num_a; //底层const 

//*p_a = 2; //错误,指向“常量”的指针不能改变所指的对象

注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。上例中指针p_a指向的内容就不是常量,可以通过赋值语句:num_a=2; 来改变它所指向的内容。

2 常量指针(顶层const):代表指针本身是常量,声明时必须初始化,之后它存储的地址值就不能再改变。声明时const必须放在指针符号*后面,即:*const 。声明常量指针就是顶层const,下面举一个例子:

int num_b = 2;
int *const p_b = &num_b; //顶层const
//p_b = &num_a; //错误,常量指针不能改变存储的地址值
其实顶层const和底层const很简单,一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。

final和override关键字:

1.final
final限定某个类不能被继承或某个虚函数不能被重写。如果修饰函数只能修饰虚函数,且要话到类或函数后面。参考如下:

struct A
{
virtual void fun() final; //该虚函数不能被重写
virtual bar() final; //err: 非虚函数不能被final修饰
};

struct B final : A
{
void fun(); //err: 该虚函数不能被重写,因为在A中已经被声明为final
};

struct C : B //err: B是final
{
};

2.override
  override关键字保证了派生类中声明重写的函数与基类虚函数有相同的签名,可避免一些拼写错误,如加了此关键字但基类中并不存在相同的函数就会报错,也可以防止把本来想重写的虚函数声明成了重载。同时在阅读代码时如果看到函数声明后加了此关键字就能立马知道此函数是重写了基类虚函数。保证重写虚函数的正确性的同时也提高了代码可读性。

struct A
{
virtual void fun();
};

struct D : A
{
void fun() override;//显示重写
};

拷贝初始化和直接初始化,初始化和赋值的区别:

拷贝初始化和直接初始化:(1)对于一般的内建类型,这两种初始化基本上没有区别。(2)当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后使用复制构造函数将那个临时对象复制到正在创建的对象

初始化和赋值的区别:对象的初始化是说你在声明的时候就调用默认的或者非默认的构造函数进行初始化工作,而赋值指的是你用一个已经存在的对象去给另一个已经存在的对象赋值。

区别的例子见上一篇随笔。

extern "C"的用法:

https://blog.csdn.net/qq_24282081/article/details/87530239

 

简单来说,c++中对函数名在编译后进行了更改,加入了返回类型,参数类型等(这样才能实现重载)。而在C中没有这个功能,因此如果定义函数的文件为.c文件,则编译后不会对函数名做处理,而如果此时main函数为cpp文件,则它在调用处对函数名进行修正,并按照这个修正的函数名去寻找函数,但由于定义函数的文件为.c,则不会更改函数名,造成寻找不到函数的问题。加入extern C后,main中就不会按照cpp的方式来更改函数名。



 

C++面试题

原文:https://www.cnblogs.com/lxy-xf/p/11055195.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!