C&C++笔面试知识点

对虚函数理解

  • 1、虚函数:定义在基类中的函数,子类必须对其进行覆盖
  • 2、作用:定义子类对象,并调用对象中未被子类覆盖的基类函数A,又调用了已被子类覆盖的基类函数B,那此时会调用基类中函数B,可本应该调用的事子类的覆盖函数B;
  • 3、在使用指向子类对象的基类指针,并调用子类中的覆盖函数时,如果该函数不是虚函数,那么将调用基类中的该函数;如果该函数时虚函数,则会调用子类中的该函数
  • 4、虚函数实际意义:疑问想要调用子类中的覆盖函数,直接通过子类对象,或者指向子类对象的子类指针来调用,要虚函数干什么?实际开发过程中,会用到别人封装好的框架和类库,可通过继承其中的类,并覆盖基类中的函数,来实现自定义的功能(连接子类函数与基类函数,在父类函数中调用子类函数)。有些函数需要框架来调用,并且API需要传入基类指针类型的参数,而使用虚函数可以将指向子类对象的基类指针来作为参数传入API,让API能够通过基类指针来调用我们自定义的子类函数。(体现了多态性)

为什么要定义虚析构函数
通过父类指针将所有子类对象析构函数都执行一遍,通过父类指针释放所有子类资源

谈谈对多态的理解

  1. 多态:(在继承父类基础上效果)同样的调用语句有多种不同的表现形态
  2. 多态实现条件:有继承、有virtual重写、有父类指针(引用)指向子类对象不同
  3. 多态的C++实现:virtual关键字,告诉编译器这个函数要支持多态不是根据指针类型判断如何调用,而是要根据指针所指向的实际对象类型来判断如何调用
  4. 多态的理论基础:动态联编和静态联编,根据实际对象类型来判断重写函数的调用;
  5. 多态的重要意义:设计模式的基础,是框架的基石;
  6. 实现多态的理论基础:函数指针做函数参数

是否可类的每个成员函数声明为虚函数,为什么?

处于效率考虑,不能,通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用函数,而普通成员函数是在编译时就确定了调用的函数,在效率上,虚函数的效率要低很多。

https://blog.csdn.net/hsq1596753614/article/details/80249605

1引用和指针有什么区别?

  • 定义一个指针变量编译器会为它分配内存,而引用不占用内存
  • 引用必须在定义时被初始化,指针不必
  • 不存在指向空值的引用,但存在指向空值的指针
#include<stdio.h>
int main(void){
    int *p;//int *p =NULL;
    printf("%d\n",sizeof(p));
}

函数传递中值传递、地址传递、引用传递有什么区别?

  • 值传递:(进入被调用函数后)会为形参重新分配内存空间,将实参的值拷贝给形参,形参的值不会影响实参的值,函数调用结束后形参被释放
  • 引用传递:不会为形参重新分配内存空间形参只是实参的别名形参的改变会影响实参的值,函数调用结束后形参不会被释放
  • 地址传递形参为指针变量,将实参的地址传递给(被调用函数)函数,可以在函数中改变实参的值,调用时为形参指针变量分配内存,结束时释放指针变量

static关键字有什么用?(C++中应用?)

  • 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时维持上次的值
  • 模块内的static全局变量可以被模块内所有函数调用,但不能被模块外其他函数访问
  • 在模块内的static函数只能被这一模块内的其他函数调用,这和函数的使用范围被限制在声明它的模块内;
  • 在类中的static成员变量属于整个类拥有,对类的对象只有一份拷贝(?);
  • 在类中的static成员函数属于整个类拥有,这个函数不接收this指针,因而只能访问static成员变量。

const关键字有什么作用?

  • const int a;(int const a;)表示一个常量,定义const变量时,要它初始化,否则右系统分配,后面使用值无法修改;
  • const int *b;(int const *b;)指针所指的数据不能被修改,但指针变量可被修改限定位置 *b
  • int *const c;指针所指的数据可被修改,但指针变量不可被修改,限定位置 *const
  • const int *const b;二者同时指定为const,表示指针和指针所指内存数据均不可被修改
  • 一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值
  • 对于类的成员函数,若指定其为const类型,则表明是一个常函数,不能改变类的成员变量
  • 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为左值;?
#include<stdio.h>
int main(){

	int a = 10;
	int b = 20;

	printf("int const * -----\n");
	const int *p = &a;//此时const限定int 
	printf("初始值: %d\n",*p); 
	printf("起始地址:%d\n",p);
//	*p = 20;//指针变量指向地址内值不能改 
//	printf("%d",p);
	p = &b; //指针变量本身地址可改变 
	printf("改变指针地址:%d\n",p);
	printf("变指针地址的值:%d\n",*p);

	printf("int *const ------------\n");
	int *const q = &a;
	printf("初始值:%d\n",*q);
	printf("起始地址:%d\n",q);
//	q = &b;//无法修改指针变量 
	*q= b;
	printf("%d\n",q);
	printf("%d\n",*q);

	printf("cosnt int *const-----\n");
	const int *const c = &a;
	printf("起始地址:%d\n",c);
	printf("初始值:%d\n",*c);
//	*c = 30;//无法修改指针指向内存数据 
//	c = &b; //无法修改指针变量 
//	printf("c:%d\n",*c);
    return 0;
}
/*
int const * -----
初始值: 10
起始地址:6487524
改变指针地址:6487520
变指针地址的值:20
int *const ------------
初始值:10
起始地址:6487524
6487524
20
cosnt int *const-----
起始地址:6487524
初始值:20
*/

小结:const关键词修饰后分为常量、常指针、指向常整形的常指针

  1. const关键词,看const 限定位置,const int *a;与int const *a;const限定的*a作用一样表示指针变量指向的内存空间的值不能被改变,但指针变量本身地址可修改;
  2. int *const b;const 被*限定为常指针,常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
  3. const int *const b;被const双重限定,指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)

疑问:指针变量地址被修改后指向空间变了,即*p的值也发生改变。

链表和数组区别在哪?(队列存储方式,顺序存储和链式存储)

  • 链表和数组都可叫线性表,数组又叫顺序表,区别在于,顺序表是在内存中开辟一段连续的空间来存储数据,而链表是靠指针来连接多块不连续的空间在逻辑上形成一片连续的空间来存储数据;
  • 数组要求空间连续,占用总空间小链表不要求空间连续,占用总空间大
  • 数组方便排序和查找,但删除和插入较慢链表方便删除和插入,范查找较慢,不方便排序

 

编写能直接实现strlen()函数功能的代码

int strlen(char *str){

for(int i=0;str[i] !=’\0’;I++)

return i;

}

编写能直接实现strstr()函数功能的代码

进程和线程的差别

  • 线程是指进程内的一个执行单元,也是进程内的可调度实体
  • 调度线程作为调度和分配的基本单位进程作为拥有资源的基本单位;
  • 并发性:不仅进程之间可以并发执行,通一个进程的多个线程也可并发执行
  • 拥有资源进程是拥有资源的一个独立单元线程不拥有系统资源但可以访问隶属于进程的资源
  • 系统开销:在创建或撤销进程时系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤销线程时的开销

关于const关键

  • char* const p;//常量指针,p的地址不可以修改
  • char const* p;//指向常量的指针,指向内容不可以改
  • Const char *p;//同char const *p;
  • Const  char *const p;//内容和地址都不可改变

 

   memset与memcpy、strcpy区别

  • memset用来对一段内存空间内全部设置为某个字符,一般用在对定义的字符串进行初始化为指定值
  • memcpy用在内存拷贝,可以用来拷贝任何数据类型的对象,可以指定拷贝的数据长度
  • strcpy只能拷贝字符串,遇到\0就结束拷贝

 

析构函数有那些特点?

  • 析构函数是特殊的类成员函数,它没有返回类型没有参数没有重载;
  • 析构函数不能手动调用,只是在类对象生命周期结束的时候,由系统自动调用释放在构造函数中分配的资源

 

虚函数有什么作用?

  • 虚函数是使子类可以用同名的函数对父类函数进行覆盖,并且在通过父类指针调用时,如果有覆盖则自动调用子类覆盖函数,如果没有覆盖则调用父类中的函数,从而实现灵活扩展和多态性
  • 如果是纯虚函数,则是纯粹是为了在子类覆盖时有个统一的命名而已子类必须覆盖纯虚函数,否则子类也是抽象类;
  • 含有纯虚函数的类称为抽象类,不能实例化对象,主要用作接口类

虚析构函数有什么作用

  • 析构函数的工作方式是最底层的派生类的析构函数最先被调用,然后调用每一个基类的析构函数
  • 在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则可能导致运行时派生类不能被销毁,然而基类部分很有可能被销毁,这导致“部分析构”现象,造成内存泄露
  • 基类一个虚析构函数删除一个派生类对象的时候就将销毁整个对象,包括父类和全部的派生类部分

分别给出bool、int、float、指针变量与零值比较的if语句

  • bool型变量:if(!var)
  • int型变量: if(var == 0)
  • float型变量:
  • const float EPSINON =0.000001;
  • If((x >= -EPSINON )&&(x<=EPSINON ))
  • 指针变量:if(var == NULL)

一下为Windows NT下32位C++程序,请计算sizeof的值

void Func(char str[100]){
    sizeof(str)=?  //答案4,32编译器跑100
}

void *p=malloc(100);
sizeof(p)=??//4

int a[100];
sizeof(p)=?//400

char *p=”aaaaaaa”;
sizoef(p)=? //4

写一个函数返回1+2+3+。。。+n的值

int sum(int n){

    return (1+n)*n/2;或者return ((long)1+n)*n/2;

}

深度变量二叉树

C++中的inline内联函数与普通函数的区别

所谓“内联函数”就是很简单的函数内嵌到调用它的程序代码中,这样做目的是节约原本函数用时的时空开销,但作为内联函数,函数体必须十分简单,不能含有循环、条件、选择等复杂的结构。

内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的,而且内联函数是真正的函数,只是在需要用到的时候内联函数想宏一样展开,所以取消了函数的参数压栈,减小了调用的开销,可以像调用函数一样调用内联函数,而不必担心会产生处理宏的一些问题。

可以用inline来定义内联函数,不过,任何在类的说明部分定义的函数都会被自动的认为是内联函数,内联函数必须是和函数体声明在一起才有效。

C++重写、重载、重定义的区别?

成员函数重载特征:相同的范围,在同一个类、函数名字相同参数不同

重写(覆盖)是指派生类函数覆盖基类函数,特征是:

不同的范围,分别位于基类和派生类中,函数名字相同、参数相同基类函数必须有virtual关键字

重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

如果派生类的函数和基类的函数同名,但是参数不同,此时不管有无virtual,基类函数被隐藏。

如果派生类的函数与基类的函数同名,且参数相同,但是基类函数没有virtual关键字,此时基类函数被隐藏。

一个数据成员是否可以既是const又是static,如果不行,为什么?

一个数据成员可以既是const又是static,表示为静态常量;

常量一般在构造函数后初始化;

静态成员一般在类外初始化;

静态常量在类外初始化,但要在类外初始化的同时声明const。

构造函数与析构函数异同点

构造函数特点如下:

构造函数名字必须与类名相同

构造函数可以有任意类型的参数,但不能返回类型

定义对象时,编译系统会自动调用构造函数;

构造函数是特殊的成员函数,函数体可以在类体内也可在类体外

构造函数被声明为公有函数,但它不能像其他成员函数那样被显式调用,它是在定义对象的同时被调用的

析构函数特点如下:

析构函数的名字必须与类名相同,但它前面必须加一个波浪号;

析构函数没有参数,也没有返回值,而且不能被重载,因此在一个类中只能有一个析构函数

当撤销对象时,编译系统会自动调用析构函数

析构函数可以是virtual,而构造函数不能是虚函数

自动调用复制构造函数的几种情况

复制构造函数的功能是用一个已知对象来初始化另一个同类的对象。复制构造函数其实也是类的构造函数,与类名相同,有且只有一个参数,是该类对象的引用,每个类必须有一个复制构造函数,如果定义类的时候没有编写,编译器编译会自动生成一个复制构造函数

复制构造函数在三种情况会被自动调用:

当类的一个对象去初始化该类的另一个对象时;

如果函数的形参是类的对象,调用函数进行形参和实参结合时;

如果函数的返回值是类对象,函数调用完成返回时

类型转换构造函数是什么?举个例子

类型转换构造函数就是自动调用类型匹配的构造函数,自动将基本数据类型转换成对象

简述C++异常处理方式

一个典型C++异常处理包含以下几个步骤

程序执行是发生错误;以一个异常对象(最简单是一个整数)记录错误的原因及相关信息

程序监测到这个错误(读取异常对象);程序决定如何处理错误

进行错误处理,并在此后恢复/终止程序的执行

成员函数和友员函数的区别

成员函数是类定义的一部分,通过特定的对象来调用。成员函数既是可以隐式访问调用对象的成员。而无须使用成员操作符;

友员函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式访问类成员,而必须将成员操作符用于作为参数传递的对象

C++中那些运算符不可以重载?
.、?:、sizeof、::、*

26、如何重载前++ 和后++运算符?
前++不带参数,后++带一个int型参数以示区分。

请说出STL标准模板版库中的几个常用类

 

函数模板与函数重载的异同?

函数的重载是指定义了几个名字相同,但参数的类型或参数的个数不同的函数;

模板函数是指的几个函数的具体算法相同,而参数类型不同的函数;

模板函数可以减少重载函数,但也可能引发错误。

C++中explicit关键字有什么作用?

explicit和构造函数一起使用,explicit指明构造函数只能显式使用,目的是为了防止不必要的隐式调用类型转换构造函数。

C++中restrict关键字有什么作用?

Restrict是用来优化的;restrict只能修饰指针,restrict修饰的指针是能够访问所指区域的唯一入口,限制多个指针指向同一地址;

面向对象的三大特征是什么?
面向对象的三个基本特征:封装、继承、多态。

什么是封装?

  • 封装是面向对象的特征之一,是对象和类概念的主要特性;
  • 封装,也就是把客观事物封装成抽象类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
  • 在C++中类中成员的属性有:public、protected、private,这三个属性的访问权限一次降低;

什么是继承?

  • 继承是指:可以使用现有类的所有功能,并在无须重新编写原来的类的情况下对这些功能进行扩展
  • 通过继承创建的类称为“子类”或“派生类”;
  • 被继承的类称为“基类”、“父类”或者”“超类”;
  • 在某些OOP语言中,一个子类可以继承多个基类,但是一般清况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现;

C++中类中成员的属性有:public、protected、private来修饰继承特性,进行属性访问控制;

什么是多态?

多态性:允许将父类对象设置为和一个或更多的它的子类相等的技术,赋值之后,父对象可以根据当前赋值给它的子对象的特性以不同方式运作,简单地说,允许将子类类型的指针赋值给父类类型的指针

实现多态的两种方式:覆盖、重载;

覆盖:子类重新定义父类的虚函数

重载:允许存在多个同名函数,而这些函数的参数表不同

类和对象的区别?
类与对象的区别,如人类与张三的区别,它们是一般与个体,抽象与具体,集体与个体的区别。

C++中namespace是什么?

Namespace命名空间

什么是可重入和不可重入函数?

什么是可重入性

可重入函数可以由多个任务并发使用,而不必担心数据错误,相反,不可重入函数不能有多个任务共享,除非能确保函数的互斥。可重入函数可以在任意时刻被中断,稍后继续运行,不会丢失数据,可重入函数要么使用本地变量,要么使用全局变量时保护自己的数据;

可重入函数

不可持续的调用持有静态数据;不返回指向静态数据的指针,所有数据都是有函数的调用者提供;使用本地数据,或者通过制作全局数据的本地拷贝和保护全局数据

如果必须访问全局变量,记住利用互斥信号量老保护全局变量

不可重入函数

  • 函数中使用了静态变量,无论是全局静态变量还是全局变量;
  • 函数返回静态变量;函数中调用了不可重入函数;
  • 函数体内使用了不可重入函数;
  • 函数体内使用了静态的数据结构;
  • 函数体内调用了malloc()或free()函数;
  • 函数体内调用了其他标准I/O函数;

总之,如果一个函数在重入条件使用了未受保护的共享资源,那么就是不可重入的;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值