语言基础C++

目录

 

● .请你说一下数组和指针的区别

参考回答:

● 请你说一说STL常用的容器

参考回答:

● 请你说一下虚函数

参考回答:

● 请你说一下动态内存分配

参考回答:

● 请你说一下深copy浅copy

参考回答:

● 请你说一下C 中申请和释放内存的方法

参考回答:

● 请你说一说C++和C的区别

参考回答:

● 请你回答一下C++中的多态是怎么实现的

参考回答:

● 请你说一下C语言的内存分配

参考回答:

● 请你回答一下什么是指针,以及指针和数组的区别,指针和引用的区别

参考回答:

● 请你说一下const和指针的区别,以及运算符优先级是怎么样的

参考回答:

● 手写代码:写一个程序算出100以内7的倍数

参考回答:

● 手写代码:写一个函数,不用加法和乘法,返回他的八倍

参考回答:

● 请你说一下new和malloc的区别

参考回答:

● 请你说一说C++语言的三大特性

参考回答:

● 请你说一说虚函数和纯虚函数区别

参考回答:

● 请你说一下static作用

参考回答:

● 请问你怎么理解多态,他有什么好处

参考回答:

● 手写代码:求两个数的最大公约数

参考回答:

● 手写代码:将字符串转int类型,要求不能用已有的方法

参考回答:

● 手写代码:求x的n次方

参考回答:


● .请你说一下数组和指针的区别

参考回答:

数组:数组是用于储存多个相同类型数据的集合。

指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。

区别:

•    赋值:同类型指针变量可以相互赋值,数组不行,只能一个一个元素的赋值或拷贝

•    存储方式:数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,多维数组在内存中是按照一维数组存储的,只是在逻辑上是多维的。指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。

•    求sizeof:数组所占存储空间的内存:sizeof(数组名),数组的大小:sizeof(数组名)/sizeof(数据类型)。在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。

•    初始化方式不同。

•    传参方式:数组传参时,会退化为指针,C语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。一级指针传参可以接受的参数类型:(1)可以是一个整形指针 (2)可以是整型变量地址 (3)可以是一维整型数组数组名;当函数参数部分是二级指针时,可以接受的参数类型:(1)二级指针变量(2)一级指针变量地址(3)一维指针数组的数组名

● 请你说一说STL常用的容器

参考回答:

(1)vector

vector是一种动态数组,在内存中具有连续的存储空间,支持快速随机访问。由于具有连续的存储空间,所以在插入和删除操作方面,效率比较慢。vector有多个构造函数,默认的构造函数是构造一个初始长度为0的内存空间,且分配的内存空间是以2的倍数动态增长的,即内存空间增长是按照20,21,22,23.....增长的,在push_back的过程中,若发现分配的内存空间不足,则重新分配一段连续的内存空间,其大小是现在连续空间的2倍,再将原先空间中的元素复制到新的空间中,性能消耗比较大,尤其是当元素是非内部数据时(非内部数据往往构造及拷贝构造函数相当复杂)。

API:

vector <T> v;//采用模板实现类实现,默认构造函数

assign(begin(),end());//将【begin(),end()】区间中的元素拷贝给本身

size();//返回元素容器中元素个数

at(int idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range异常

(2)deque

deque和vector类似,支持快速随机访问。二者最大的区别在于,vector只能在末端插入数据,而deque支持双端插入数据。deque的内存空间分布是小片的连续,小片间用链表相连,实际上内部有一个map的指针。deque空间的重新分配要比vector快,重新分配空间后,原有的元素是不需要拷贝的。

API:

deque<T>deqT;//默认构造形式

assign(begin,end);//将【begin,end】区间的数据拷贝赋值给自身

deque.size();//返回容器中元素的个数

push_back(elem);//在容器尾部添加一个数据

(3)list

list是一个双向链表,因此它的内存空间是可以不连续的,通过指针来进行数据的访问,这使list的随机存储变得非常低效,因此list没有提供[]操作符的重载。但list可以很好地支持任意地方的插入和删除,只需移动相应的指针即可。

API:

list<T>lstT;//list采用模板类实现对象的默认构造函数

push_back(elem);//在容器尾部加入一个元素

size();//返回元素容器中元素个数

empty();//判断容器是否为空

(4)map

map是一种关联容器,该容器用唯一的关键字来映射相应的值,即具有key-value功能。map内部自建一棵红黑树(一种自平衡二叉树),这棵树具有数据自动排序的功能,所以在map内部所有的数据都是有序的,以二叉树的形式进行组织。

API:

map<t1,T2>mapT;//map默认构造函数

size();//返回元素中元素的数目

empty();//判断容器是否为空

clear();//删除所有元素

(5)set

set也是一种关联性容器,它同map一样,底层使用红黑树实现,插入删除操作时仅仅移动指针即可,不涉及内存的移动和拷贝,所以效率比较高。set中的元素都是唯一的,而且默认情况下会对元素进行升序排列。所以在set中,不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,再插入新元素。不提供直接存取元素的任何操作函数,只能通过迭代器进行间接存取。

API:

set<T>sT;//set默认构造函数

size();//返回容器中元素的数目

empty();//判断容器是否为空

insert(elem);//在容器中插入元素

clear();//清除所有元素

(6)queue

queue是一个队列,实现先进先出功能,queue不是标准的STL容器,却以标准的STL容器为基础。queue是在deque的基础上封装的。之所以选择deque而不选择vector是因为deque在删除元素的时候释放空间,同时在重新申请空间的时候无需拷贝所有元素。

API:

queue<T>queT;//queue采用模板类实现,queue对象的默认构造形式

push(elem);//往队尾添加元素

(7)stack

stack是实现先进后出的功能,和queue一样,也是内部封装了deque,这也是为啥称为容器适配器的原因吧(纯属猜测)。自己不直接维护被控序列的模板类,而是它存储的容器对象来为它实现所有的功能。stack的源代码原理和实现方式均跟queue相同。

API:

stack<T>stkT;//采用模板类实现,stack对象的默认构造形式

push(elem);//向栈顶添加元素

● 请你说一下虚函数

参考回答:

拥有Virtual 关键字的函数称之为虚函数,虚函数的作用是实现动态绑定的,也就是说程序在运行的时候动态的的选择合适的成员函数。要成为虚函数必须满足两点,一就是这个函数依赖于对象调用,因为虚函数就是依赖于对象调用,因为虚函数是存在于虚函数表中,有一个虚函数指针指向这个虚表,所以要调用虚函数,必须通过虚函数指针,而虚函数指针是存在于对象中的。二就是这个函数必须可以取地址,因为我们的虚函数表中存放的是虚函数函数入口地址,如果函数不能寻址,就不能成为虚函数。

● 请你说一下动态内存分配

参考回答:

在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

● 请你说一下深copy浅copy

参考回答:

浅拷贝是增加了一个指针,指向原来已经存在的内存。而深拷贝是增加了一个指针,并新开辟了一块空间让指针指向这块新开辟的空间。浅拷贝在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误。

● 请你说一下C 中申请和释放内存的方法

参考回答:

C++:

new运算符申请内存:

将调用相应的operator new(size_t) 函数动态分配内存,在分配到的动态内存块上 初始化 相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用 operator delete(void*, void*) 函数释放已经分配到的内存。

delete运算符释放内存:

调用相应类型的析构函数,处理类内部可能涉及的资源释放,调用相应的operator delete(void *) 函数。

C:

内存区域可以分为栈,堆,静态存储区和常量存储区。局部变量,函数形参,临时变量都是在栈上获得内存的,它们获取的方式都是由编译器自动执行的。

而C标准函数库提供了许多函数来实现对堆上内存管理,其中包括:malloc函数,free函数,calloc函数和realloc函数。使用这些函数需要包含头文件stdlib.h。

(1)malloc函数

malloc函数可以从堆上获得指定字节的内存空间,其函数声明如下:

void * malloc(int n);

其中,形参n为要求分配的字节数。如果函数执行成功,malloc返回获得内存空间的首地址;如果函数执行失败,那么返回值为NULL。由于malloc函数值的类型为void型指针,因此,可以将其值类型转换后赋给任意类型指针,这样就可以通过操作该类型指针来操作从堆上获得的内存空间。需要注意的是,malloc函数分配得到的内存空间是未初始化的。因此,一般在使用该内存空间时,要调用另一个函数memset来将其初始化为全0。memset函数的声明如下:

void * memset (void * p,int c,int n) ;

该函数可以将指定的内存空间按字节单位置为指定的字符c。其中,p为要清零的内存空间的首地址,c为要设定的值,n为被操作的内存空间的字节长度。

(2)free函数

从堆上获得的内存空间在程序结束以后,系统不会将其自动释放,需要程序员来自己管理。一个程序结束时,必须保证所有从堆上获得的内存空间已被安全释放,否则,会导致内存泄露。

void free (void * p);

由于形参为void指针,free函数可以接受任意类型的指针实参。

但是,free函数只是释放指针指向的内容,而该指针仍然指向原来指向的地方,此时,指针为野指针,如果此时操作该指针会导致不可预期的错误。安全做法是:在使用free函数释放指针指向的空间之后,将指针的值置为NULL。

(3)calloc函数

calloc函数的功能与malloc函数的功能相似,都是从堆分配内存。其函数声明如下:

void *calloc(int n,int size);

函数返回值为void型指针。如果执行成功,函数从堆上获得size X n的字节空间,并返回该空间的首地址。如果执行失败,函数返回NULL。该函数与malloc函数的一个显著不同时是,**calloc函数得到的内存空间是经过初始化的,其内容全为0。**calloc函数适合为数组申请空间,可以将size设置为数组元素的空间长度,将n设置为数组的容量。

(4)realloc函数

realloc函数的功能比malloc函数和calloc函数的功能更为丰富,可以实现内存分配和内存释放的功能,其函数声明如下:

void * realloc(void * p,int n);

其中,指针p必须为指向堆内存空间的指针,即由malloc函数、calloc函数或realloc函数分配空间的指针。realloc函数将指针p指向的内存块的大小改变为n字节。如果n小于或等于p之前指向的空间大小,那么。保持原有状态不变。如果n大于原来p之前指向的空间大小,那么,系统将重新为p从堆上分配一块大小为n的内存空间,同时,将原来指向空间的内容依次复制到新的内存空间上,p之前指向的空间被释放。relloc函数分配的空间也是未初始化的。

● 请你说一说C++和C的区别

参考回答:

•    C是面向过程的语言,而C++是面向对象的语言

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

•    C中的struct和C++的类,C++的类是C所没有的,但是C中的struct是可以在C++中正常使用的,并且C++对struct进行了进一步的扩展,使struct在C++中可以和class一样当做类使用,而唯一和class不同的地方在于struct的成员默认访问修饰符是public,而class默认的是private;

•    C++支持函数重载,而C不支持函数重载,而C++支持重载的依仗就在于C++的名字修饰与C不同,例如在C++中函数int fun(int ,int)经过名字修饰之后变为 _fun_int_int ,而C是 _fun,一般是这样的,所以C++才会支持不同的参数调用不同的函数;

•    C++中有引用,而C没有;

•    C++全部变量的默认链接属性是外链接,而C是内连接;

•    C 中用const修饰的变量不可以用在定义数组时的大小,但是C++用const修饰的变量可以

● 请你回答一下C++中的多态是怎么实现的

参考回答:

C++的多态性是用虚函数和延迟绑定来实现的,在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

1. 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。

2. 存在虚函数的类都有一个一维的虚函数表叫做虚表。类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。

3. 多态性是一个接口多种实现,是面向对象的核心。分为类的多态性和函数的多态性。

4. 多态用虚函数来实现,结合动态绑定。

5. 纯虚函数是虚函数再加上= 0。

6. 抽象类是指包括至少一个纯虚函数的类。

● 请你说一下C语言的内存分配

参考回答:

在C语言中,对象可以使用静态或动态的方式分配内存空间

静态分配:编译器在处理程序源代码时分配,由于是在程序执行之前进行所以效率比较高

动态分配:程序在执行时调用malloc库函数申请分配,可以灵活的处理未知数目的对象

静态与动态内存分配的主要区别如下:

静态对象是有名字的变量,可以直接对其进行操作;动态对象是没有名字的变量,需要通过指针间接地对它进行操作。

静态对象的分配与释放由编译器自动处理;动态对象的分配与释放必须由程序员显式地管理,它通过malloc()和free两个函数(C++中为new和delete运算符)来完成。

● 请你回答一下什么是指针,以及指针和数组的区别,指针和引用的区别

参考回答:

指针就是一个存放地址的变量,当指针指向某个变量,这时这个指针里就存放了那个变量的地址

指针与数组的区别

1、指针和数组的分配,数组是开辟一块连续的内存空间,指针不是

2、空间分配,   这里又分为两种情况。
第一,如果是全局的和静态的
char *p = “hello”;
这是定义了一个指针,指向rodata section里面的“hello”,可以被编译器放到字符串池。在汇编里面的关键字为.ltorg。意思就是在字符串池里的字符串是可以共享的,这也是 编译器优化的一个措施。
char a[] = “hello”;
这是定义了一个数组,分配在可写数据块,不会被放到字符串池。
第二,如果是局部的
char *p = “hello”;
这是定义了一个指针,指向rodata section里面的“hello”,可以被编译器放到字符串池。在汇编里面的关键字为.ltorg。意思就是在字符串池里的字符串是可以共享的,这也是 编译器优化的一个措施。另外,在函数中可以返回它的地址,也就是说,指针是局部变量,但是它指向的内容是全局的。
char a[] = “hello”;
这是定义了一个数组,分配在堆栈上,初始化由编译器进行。(短的时候直接用指令填充,长的时候就从全局字符串表拷贝),不会被放到字符串池(同样如前,可 能会从字符串池中拷贝过来)。注意不应该返回它的地址。

3、使用方法

如果是全局指针,用于不需要修改内容,但是可能会修改指针的情况。如果是全局数组,用于不需要修改地址,但是却需要修改内容的情况。如果既需要修改指针,又需要修改内容,那么就定义一个数组,再定义一个指针指向它就可以了。

指针和引用的区别

1、指针是一个变量,只不过这个变量存储的是一个地质,而引用跟原来的变量实质上是一个东西,只不过是原变量的一个别名

2、引用不可以为空,当被创建的时候,必须初始化,而指针可以是空

3、指针可以有多级,但是引用只有一级

4、指针的值在初始化后可以改变,但是引用进行初始化后就不会再改变了

5、sizeof引用得到的是指向变量的大小,而指针得到的是本身的大小

6、如果返回动态分配的对象或内存,必须使用指针,否则可能引起内存泄漏

● 请你说一下const和指针的区别,以及运算符优先级是怎么样的

参考回答:

const和指针的区别

以下三个语法的区别

(1) const int *p1;

(2) int const *p2;

(3) int *const p3;

由于指针*p的赋值方式有两种
第一种是:p = &a;
第二种是:*p = a;

(1)和(2)的效果是一样的。只能通过第一种方式来修改指针指向的变量

而(3)的方式是只能在一开始的时候指定一个变量,以后不能再指向其他变量。

其实主要是看const后面的变量是什么,只有const后面的变量无法修改

运算符优先级,简单来说就是!>算术运算符>关系运算符>&&>||>赋值运算符

● 手写代码:写一个程序算出100以内7的倍数

参考回答:

void beishu()

{
int i ;
i=7;
while(i<=100)
{
if((i%7)==0)
printf("%d",i);
i+=7;
}
}

● 手写代码:写一个函数,不用加法和乘法,返回他的八倍

参考回答:

import org.junit.Test;

public class solution {

@Test
public void testFunc(){
int i = 1;
int res = beiShu(i);
System.out.println("res: "+res);
}

public int beiShu(int x){
return x<<3;
}


}

● 请你说一下new和malloc的区别

参考回答:

1、属性:new是C++关键字,需要编译器支持,malloc是库函数,需要头文件支持

2、参数:使用new操作符申请内存分配时无需指定内存块的大小,编译器会根据类型信息自行计算,malloc则需要显式的指出所需内存的尺寸

3、返回类型:new返回的是对象类型的指针,malloc返回void*,需要通过强制类型转换将void*指针转换成我们需要的类型

4、分配失败:new内存分配失败时,会跑出bac_alloc异常,malloc分配内存失败返回null

5、 自定义类型

new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。

malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

6、 重载

C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。

7、内存区域

new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

● 请你说一说C++语言的三大特性

参考回答:

封装,继承,多态

● 请你说一说虚函数和纯虚函数区别

参考回答:

虚函数和纯虚函数区别

观点一:类里声明为虚函数的话,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被重载,这样的话,这样编译器就可以使用后期绑定来达到多态了
纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。
class A{
protected:
void foo();//普通类函数
virtual void foo1();//虚函数
virtual void foo2() = 0;//纯虚函数
}
观点二:虚函数在子类里面也可以不重载的;但纯虚必须在子类去实现,这就像Java的接口一样。通常我们把很多函数加上virtual,是一个好的习惯,虽然牺牲了一些性能,但是增加了面向对象的多态性,因为你很难预料到父类里面的这个函数不在子类里面不去修改它的实现

观点三:虚函数的类用于“实作继承”,继承接口的同时也继承了父类的实现。当然我们也可以完成自己的实现。纯虚函数的类用于“介面继承”,主要用于通信协议方面。关注的是接口的统一性,实现由子类完成。一般来说,介面类中只有纯虚函数的。
观点四:带纯虚函数的类叫虚基类,这种基类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。这样的类也叫抽象类。
虚函数是为了继承接口和默认行为
纯虚函数只是继承接口,行为必须重新定义

● 请你说一下static作用

参考回答:

Static作用:

1、隐藏,当同时编译多个文件时,所有未加static的全局变量和函数都具有全局可见性。

2、保持变量内容的持久,存储在静态数据区的变量会在程序放开是运行时就完成初始化,也是唯一一次初始化,共有两种变量存储在静态存储区,全局变量和static变量,PS:如果static局部变量在函数内定义,他的生存期为整个源程序,但其作用域和自动变量相同,只能在定义该变量的函数内使用,退出该函数后,尽管该变量还存在,但是不能使用。

3、默认初始化为0,

● 请问你怎么理解多态,他有什么好处

参考回答:

所谓多态,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行时确定,即一个引用变量到底会指向哪个类的实例对象,调用哪个类的实现方法,由程序运行期间才确定,这样不用修改程序源代码就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性

● 手写代码:求两个数的最大公约数

参考回答:

int division(int n,int m)

{

if(n<m)

division考(m,n); //交换m与n

else if(m==0)

return n;

else

{

int temp=n;

n=m;

m=temp%n;

division(n,m); //重复上述过程

}

}

● 手写代码:将字符串转int类型,要求不能用已有的方法

参考回答:

public static int stringToInt(String str) {

int result=0;

char[] ch=str.toCharArray();

int len=ch.length;

for(int i=0;i<len;i++) {

result+=(((int)ch[i]-'0')*Math.pow(10, len-1-i));

}

return result;

}

● 手写代码:求x的n次方

参考回答:

double Pow(double x, int n)

{

double result = 1;

while (n)

{

if (n & 1)        // 等价于 if (n % 2 != 0)

result *= x;

n >>= 1;          //右移一位相当于n/2(类比十进制来理解)

x *= x;

}

return result;

}

猜你喜欢

转载自blog.csdn.net/u012369559/article/details/89494363
今日推荐