构造函数的作用是什么?
主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。构造函数不能被直接调用,创建对象时才会自动调用;而一般的方法是在程序执行到它的时候被调用的;
什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?
用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。
1). 使用的时候要记得指针的长度.
2). malloc的时候得确定在那里free.
3). 对指针赋值的时候应该注意被赋值指针需要不需要释放.
4). 动态分配内存的指针最好不要再次赋值.
5). 在C++中应该优先考虑使用智能指针.
内存溢出的原因?
内存溢出是指在申请内存空间时出现没有足够的内存空间供其使用的情况,内存溢出的原因大致有如下4种:
(1) 内存中加载的数据量过于庞大,例如一次从数据库中取较多的数据
(2) 代码中出现死循环或者循环产生过多重复的对象实体
(3) 递归调用的层次太深导致堆栈溢出
(4) 内存泄漏导致内存溢出
c++静态成员的作用?怎么使用的?
C++提供了静态成员,用以解决同一个类的不同对象之间数据成员和函数的共享问题。
静态成员的特点是:不管这个类创建多少个对象,其静态成员在内存中只保留一份副本,这个副本为该类的所有对象所共享。
静态函数是用static修饰符修饰的函数,静态函数没有this指针,只能访问静态变量。类中如果函数调用的结果不会访问或者修改任何对象数据成员,这样的成员声明为静态成员函数比较好。
如何存储一个远超当前数据类型范围的数字。
存储:利用数组,可以用char数组一位一位地存放,记住第几个数开始是小数点分割点即可,或者使用字符串。
指针与引用的区别?
相同点:
1). 都是地址的概念;
2). 都是“指向”一块内存。指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名;
3). 引用在内部实现其实是借助指针来实现的,一些场合下引用可以替代指针,比如作为函数形参。
不同点:
1). 指针是一个实体,而引用(看起来,这点很重要)仅是个别名;
2). 引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
3). 引用不能为空,指针可以为空;
4). “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
5). 指针和引用的自增(++)运算意义不一样;
6). 引用是类型安全的,而指针不是 (引用比指针多了类型检查)
7). 引用具有更好的可读性和实用性。
类与结构体有什么区别?
1.类中能定义函数而结构体不能
2.类中数据有访问权限,结构体中可随意访问
3.结构体存储在栈中,类的实例化可以存储在栈中,也可以存储在堆中;
4.结构体的执行效率比类要高;
5.结构体没有析构函数,类有析构函数;
6.结构体不可以继承,类可以继承。
引用与值传递的区别?
答:值传递传递的是一个值的副本,函数对形参的操作不会影响实参的值;引用传递传递的是引用对象的内存地址,函数对形参的操作会影响实参的值,实参的值会随着形参的值得改变而改变。
什么是函数模板?
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
什么是类模板?
答:所谓类模板,实际上是建立一个通用类,其数据成员、成员函数的返回类型和形参类型不具体指定,用一个虚拟的类型来代表。 使用类模板定义对象时,系统会根据实参的类型来取代类模板中虚拟类型从而实现了不同类的功能。
什么是泛型编程?
答:泛型编程就是以独立于特定类实现的方式编写代码,针对不同的类型提供通用的实现。
C++如何实现泛型编程?
答:C++中泛型编程的实现是使用C++中的模板技术来实现的,主要是设计函数模板和类模板。
C++覆盖与隐藏概述?
(1)覆盖指的是在子类和父类中,存在函数名、参数均相同的函数,并且父类的该函数为虚函数;(2)隐藏指的是在子类与父类中,存在函数名相同、参数不同的函数,此时无论父类函数是否为虚函数,父类函数都会被被隐藏,或者存在函数名、参数均相同的函数,此时只有当父类函数不为虚函数时,父类函数才会被隐藏。
C++继承中重载、重写、重定义的区别:
(1)成员函数重载特征:
a.相同的范围,在同一个类
b.函数名字相同
c.参数不同
(2)重写(覆盖)是指派生类函数覆盖基类函数,特征是:
a.不同的范围,分别位于基类和派生类中
b.函数的名字相同
c.参数相同
d.基类函数必须有virtual关键字
(3)重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
a.如果派生类的函数和基类的函数同名,但是参数不同,此时不管有无virtual,基类的函数被隐藏;
b.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类函数被隐藏。
结构与联合有和区别?
(1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
(2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。
描述内存分配方式以及它们的区别?
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多
请说出const与#define 相比,有何优点?
答案:const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
- const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。
- 而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
- 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
宏定义与操作符的区别?
答:宏定义是C++的预处理命令之一,它是一个替换操作,不做计算和表达式求解,不占内存和编译时间。
虚函数与纯虚函数的特点?
答:虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public;
纯虚函数是虚函数的一个子集,含有纯虚函数的类就是抽象类,它不能生成对象。
如何使用纯虚函数?
答:纯虚函数用来定义没有意义的实现,用于抽象类中需要交给派生类具体实现的方法。
纯虚函数没有函数体,没有定义具体的操作内容。
抽象类的作用?
抽象类不能实例化,但是可定义抽象类的指针和引用,发挥动态特性。
抽象类作为一个类族的公共接口,通过接口的完整实现(即派生类编写纯虚函数体)--->>接口调用实现动态特性。(一个函数指针可以被赋予不同函数的入口地址,实际被调用的函数一般到了运行时才能确定)
虚函数和动态绑定的关系?
虚函数是动态绑定的基础,虚函数是为了实现运行过程中的多态。
只有通过基类的指针或者引用调用虚函数时,才会发生动态绑定。
基类的指针可以指向派生类的对,基类的引用可作为派生类对象的别名。
虚函数有什么作用?
虚函数的作用:是在程序的运行阶段动态地选择合适的成员函数。实现动态多态性。
(1)虚函数的功能是使子类可以用同名的函数对父类函数进行覆盖,并且在通过父类指针调用时,如果有覆盖则自动调用子类覆盖函数,如果没有覆盖则调用父类中的函数,从而实现灵活扩展和多态性;
(2)如果是纯虚函数,则纯粹是为了在子类覆盖时有个统一的命名而已,子类必须覆盖纯虚函数(编写函数体),则否子类也是抽象类;
(3)含有纯虚函数的类称为抽象类,不能实例化对象,主要用作接口类。
虚函数的实质:指向基类的指针在指向派生类对象时,自动进行指针类型转换,将派生类的对象的指针先转换为基类的指针,这样基类指针指向的是派生类对象中的基类部分。
虚析构函数有什么作用?
简单说就是做一些扫尾(清理)工作。
(1)析构函数的工作方式是:最底层的派生类的析构函数最先被调用,然后调用每一个基类的析构函数;
(2)在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则可能导致运行时派生类不能被销毁。然而基类部分很有可能已经被销毁,这就导致“部分析构”现象,造成内存泄漏;(归还空间)
(3)给基类一个虚析构函数,删除一个派生类对象的时候就将销毁整个对象,包括父类和全部的派生类部分。
虚基类及其作用
引入虚基类的原因:如果一个派生类有多个直接基类,而这些直接基类又有一个共同基类,则最后的派生类中会保留其直接基类所继承的共同基类的多份同名成员,而引用这些同名的成员时,必须在派生类对象名后增加直接基类名以免产生二义性。占用大量的存储空间,易出错。
将共同基类设置为虚基类,这样从不同派生类继承来的同名数据成员在内存中就只有一个拷贝,同名函数也只有一种映射。只维护一份成员副本
为了保证虚基类在派生时只被继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则仍然会出现对基类的多次继承。
基类的析构函数不是虚函数,会带来什么问题?
派生类的析构函数用不上,会造成资源的泄漏。
析构函数有哪些特点?
(1)析构函数也是特殊的类成员函数,它没有返回类型;
(2)没有参数;
(3)没有重载;
(4)public、private、protected等权限控制对析构函数无效;
(5)析构函数不能手动调用,只是在类对象生命周期结束的时候,由系统自动调用释放在构造函数中分配的资源。
构造函数与析构函数的异同点
1.构造函数有如下特点:
(1)构造函数的名字必须与类名相同;
(2)构造函数可以有任意类型的参数,但不能有返回类型;
(3)定义对象时,编译系统会自动调用构造函数;
(4)构造函数是特殊的成员函数,函数体可以在类体内也可以在类体外;
(5)构造函数被声明为公有函数,但它不能像其他成员函数那样被显式调用,它是在定义对象的同时被调用的。
2.析构函数有如下特点:
(1)析构函数的名字必须与类名相同,但它前面必须加一个波浪号;
(2)析构函数没有参数,也没有返回值,而且不能被重载,因此在一个类中只能有一个析构函数;
(3)当撤销对象时,编译系统会自动调用析构函数;
(4)析构函数可以是virtual,而构造函数不能是虚函数。
堆和栈的区别?
1).堆存放动态分配的对象——即那些在程序运行时动态分配的对象,比如 new 出来的对象,其生存期由程序控制;
2).栈用来保存定义在函数内的非static对象,如局部变量,仅在其定义的程序块运行时才存在;
3).静态内存用来保存static对象,类static数据成员以及定义在任何函数外部的变量,static对象在使用之前分配,程序结束时销毁;
4).栈和静态内存的对象由编译器自动创建和销毁。
全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
生命周期不同:全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;
使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。
操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。
static作用有以下两个:
Static:用static修饰的内容在内存中独立存储,优先于对象产生,属于类而不属于任何一个对象,可以被类中的对象所共享。
1.规定内容的作用区域,就是内容所在的函数或类,只有在该函数/类才能调用。
2.规定内容的生命周期,内容在函数/类执行完毕后不会被回收,会一直保留,直至程序结束
C中static有什么作用
(1)隐藏。 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
(2)static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量。
(3)static的第三个作用是默认初始化为0.其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0×00,某些时候这一特点可以减少程序员的工作量。
静态数据成员与全局变量的不同
全局变量可以被任意的类或函数调用,静态数据成员只能在其所用范围(类中)被调用。
什么是静态函数?如何使用静态函数?
答:静态函数是用static修饰符修饰的函数,静态函数没有this指针,只能访问静态变量。类中如果函数调用的结果不会访问或者修改任何对象数据成员,这样的成员声明为静态成员函数比较好。
C++中const有什么用?
不要一听到const就说是常量,这样给考官一种在和一个外行交谈的感觉。应该说const修饰的内容不可改变就行了, 定义常量只是一种使用方式而已,还有const数据成员,const参数, const返回值, const成员函数等, 被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
C与C++各自是如何定义常量的?有什么不同?
C中是使用宏#define定义, C++使用更好的const来定义。
区别:
1)const是有数据类型的常量,而宏常量没有,编译器可以对前者进行静态类型安全检查,对后者仅是字符替换,没有类型安全检查,而且在字符替换时可能会产生意料不到的错误(边际效应)。
2)有些编译器可以对const常量进行调试, 不能对宏调试。
既然C++中有更好的const为什么还要使用宏?
const无法代替宏作为卫哨来防止文件的重复包含。
#include<a.h>和#include"a.h" 有什么区别?
答:对于#include <a.h> ,编译器从标准库路径开始搜索 a.h
对于#include "a.h" ,编译器从用户的工作路径开始搜索 a.h
C++中的什么是多态性? 是如何实现的?
C++中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。
C++实现多态性:
静态多态性:是通过函数的重载来实现的,由函数重载和运算符重载形成的多态性属于静态多态性,在系统编译时系统就知道要调用哪个函数,又称编译时的多态性。
动态多态性:在编译时不确定调用的是哪个函数,而是在程序运行过程中动态地确定操作所针对的对象,又称运行时的多态性(通过虚函数实现)虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。
Java实现运行时多态性主要通过重写、重载和接口三种主要实现方式。
多态的优点:1.增强了代码的重用性和灵活性;2.同一参数类型;3.同一功能调用;4.降低耦合性。
那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。
什么是动态特性?
在绝大多数情况下, 程序的功能是在编译的时候就确定下来的, 我们称之为静态特性。 反之, 如果程序的功能是在运行时刻才能确定下来的, 则称之为动态特性。C++中, 虚函数,抽象基类, 动态绑定和多态构成了出色的动态特性。
什么是封装?C++中是如何实现的?
封装目的:模块化+信息隐藏
封装:隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互,将数据和操作数据的方法进行有机结合。 是通过特性和行为的组合来创建新数据类型让接口与具体实现相隔离。
C++中是通过类来实现的, 为了尽量避免某个模块的行为干扰同一系统中的其它模块,应该让模块仅仅公开必须让外界知道的接口。
什么是深浅拷贝?
浅拷贝是创建了一个对象用一个现成的对象初始化它的时候只是复制了成员(简单赋值)而没有拷贝分配给成员的资源(如给其指针变量成员分配了动态内存)两个指针指向同一个内存空间,没有形成真正的副本;
深拷贝是当一个对象创建时,如果分配了资源,就需要定义自己的拷贝构造函数,使之不但拷贝成员也拷贝分配给它的资源。
面向对象程序设计的优点?
面向对象编程的编码具有高可重用性,可以在应用程序中大量采用成熟的类库(如STL),从而虽短了开发时间,软件易于维护和升级。
总结:开发时间短, 效率高, 可靠性高,代码可重用性高。
面向对象与面向过程的区别?
答:面向过程是一种以过程为中心的编程思想,以算法进行驱动;面向对象是一种以对象为中心的编程思想,以消息进行驱动。面向过程编程语言的组成:程序=算法+数据;面向对象编程语言的组成:程序=对象+消息。
C++支持参数个数不确定的函数吗?
答:C++可以通过隐藏参数机制支持参数不确定的函数。
什么是内联函数?
答:在类声明的内部声明或定义的成员函数叫做内联(inline)函数,在内联函数内不允许有循环语句和switch语句。
内联函数和宏定义的区别?
宏定义是在预编译时把所有的宏名用宏体来替换,简单来说就是字符串的替换。
内联函数是在编译时进行代码插入,在调用内联函数的地方直接把内联函数的内容展开,省去了函数调用时的压栈和出栈操作,提高了效率。内联函数是嵌入代码,在调用函数时不进行跳转而是把内联函数的代码插入到相应位置。内联函数在编译时要进行参数类型检查,因此内联函数更安全、可靠,但这是牺牲空间来换取的性能提升。
链表和数组的区别在哪里?
- 链表和数组都可以叫线性表,数组又叫顺序表,主要区别在于,顺序表是在内存中开辟一段连续的空间来存储数据,而链表是靠指针来连接多块不连续的空间,在逻辑上形成一片连续的空间来存储数据;
- 数组要求空间连续,占用总空间小,链表不要求空间连续,占用总空间大;
- 数组方便排序和查找,但删除和插入较慢;链表方便删除和插入,但查找较慢,不方便排序。
友元关系是什么?
友元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。
友元提供了一种 普通函数或者类成员函数 访问另一个类中的私有或保护成员 的机制。也就是说有两种形式的友元:
(1)友元函数:普通函数对一个访问某个类中的私有或保护成员。
(2)友元类:类A中的成员函数访问类B中的私有或保护成员。
成员函数和友元函数的区别
(1)成员函数是类定义的一部分,通过特定的对象来调用。成员函数既可以隐式访问调用对象的成员,而无须使用成员操作符;
(2)友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式访问类成员,而必须将成员操作符用于作为参数传递的对象。
类与对象的区别?
类与对象的区别,如人类与张三的区别,它们是一般与个体、抽象与具体、集体与个体的区别。
C++中namespace是什么?
namespace命名空间,是C++的语言特性,可以设置数据成员的作用域。
变量的声明和定义有什么区别
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。
一个变量可以在多个地方声明,但是只在一个地方定义。
加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。
Java与C++的区别:
(1)JAVA中没有显式的指针,但是C++中有
(2)JAVA继只能支持单承,但是C++支持多继承(一个类可以继承多个类)
(3)JAVA中有自动的垃圾回收机制,用完一个内存之后无需自己释放,但是C++需要自己delete或者free,所以可能出现内存泄漏
基类和派生类
C++一个主要目标是提供代码重用,类继承是实现该目标的重要途径之一。从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。
派生类和基类的关系:
1. 派生类对象存储了基类的数据成员
2. 派生类对象可以使用基类的方法
既然需要构建派生类,说明原有的基类不能满足程序的需要,所以派生类要有自己的构造函数,可以额外添加需要的数据成员和成员函数。
派生类和基类的特殊关系:
1.基类指针可以在不进行显式类型转换的情况下指向派生类对象;
2.基类引用可以在不进行显式类型转换的情况下引用派生类对象;
什么是多继承(C++问题)
多继承(Multiple inheritance),即一个子类可以有多个父类,它继承了多个父类的特性。多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。
继承下的构造函数:多继承形式下的构造函数和单继承形式基本相同,只是要在派生类的构造函数中调用多个基类的构造函数。基类构造函数的调用顺序和和它们在派生类构造函数中出现的顺序无关,而是和声明派生类时基类出现的顺序相同。
命名冲突:当两个或多个基类中有同名的成员时,如果直接访问该成员,就会产生命名冲突,编译器不知道使用哪个基类的成员。这个时候需要在成员名字前面加上类名和域解析符,以显式地指明到底使用哪个类的成员,消除二义性。
继承概念的实现方式有哪些?
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
实现继承是指使用基类的属性和方法而无需额外编码的能力;
接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。