面试题(1)

  • C++的基础数据类型分为3种:整数型、浮点型和void型
  • 除了指针类型与long随操作系统字长变化而变化外,其他的都固定不变(32位和64相比)
  • 表示整数、字符和布尔值的算术类型合称为整形,整形根据修饰符细分为13种形式,它们的区分主要体现在类型名、长度和表示范围几个方面。
  • C++常量:常量是不可改变的数据,主要分为整形常量,浮点型常量,字符型常量,字符串常量,转义字符常量(’\0nnn’),地址常量
  • 常量和变量的区别:常量是不可以改变值的量,变量是可以改变值的量,常量在定义时必须初始化,变量可以在定义时不初始化。常量本身没有地址属性(除字符串常量等),不可以寻址,变量可以寻址。常量有相对较高编译执行效率。

  • 哪些操作符?:表达式是由操作符、操作数字和标点符号组成的序列;按照功能区分:算术、关系、逻辑、位、赋值、自增和自减,条件,sizeof和逗号操作符;按照操作数:一元、二元和三元操作符

  • 操作符优先级对运算结果的影响:优先级决定了表达式的运算顺序,不同优先级的运算符的不同使用会影响表达式的运算结果
  • 逻辑操作符:逻辑操作只返回true或false这两种值,它们可以用来做判断条件,逻辑操作符不会改变操作数的值。
  • 自增自减前操作与后操作的区别:前自增自减操作的优先级大于赋值运算符,而后自增自减的优先级小于赋值运算符,后自增自减操作后表达式的值不会发生改变
  • 指针自增自减的不同:变量的自增自减是修改变量的值,指针的自增自减是修改指针的指向地址

  • 什么是左值和右值:变量是左值,可以出现在赋值语句的左边或右边,也就是说左值可以当右值使用;数字字面量是右值,不能出现在赋值语句的左边,不能被赋值

  • 什么是变量:变量是一段实际连续存储空间的别名,程序中通过变量来申请并命名存储空间,通过变量的名字可以使用存储空间
  • 变量的初始化方式:C++支持两种初始化变量的形式即赋值初始化和直接初始化(把初始化值放在括号中)。如果变量在定义的时候没有被初始化(局部变量,静态局部变量,全局变量)
  • 变量默认初始化有什么规则:系统有时候会自动初始化未初始化的变量。使用未初始化的变量是常见的程序错误,建议每个内置类型的对象都要初始化。
  • 什么是变量的声明和定义:C++区分了声明和定义。定义和声明有相同的时候,但声明的主要目的是表明变量的类型和名称,而定义的主要目的是为变量分配存储空间,还可以给变量初始化
  • C++中有哪几种作用域:作用域用于区别变量名的不同有效范围,分为全局作用域,局部作用域和语句作用域(for语句内的i)
  • 局部变量与嵌套的作用域:局部变量就是具有局部作用域的变量。在C++中,作用域是可以嵌套的(定义在全局作用域的变量名可以在局部作用域中使用)
  • 变量有哪几种存储类型:自动类型变量(auto),静态类型变量(static),寄存器类型变量(register),外部类型变量(extern)

  • 什么是引用:引用是变量的别名,主要用作函数的形参来使用

  • const引用和非const引用:const引用是指向const对象的引用,const引用必须被定义为const类型(const int a = 100;const int& refa = a; );const引用让变量拥有只读属性
    这里写图片描述

  • C与C++的区别:C语言是结构化的编程语言,它是面向过程的,考虑的是实现过程;C++是面向对象的,考虑的是整个程序模型

  • 匈牙利命名法是一种编程时的命名规范,主要思想是在变量和函数名中加入前缀以增进对程序的理解
  • 变量命名规则:变量名由字母、数字和下划线组成并且需以字母或下划线开头,变量名不可以与保留关键字和标准库冲突

  • 条件语句的几种形式:if,if…else和if…else if..else

  • 条件语句如何嵌套?如何匹配else子句:条件语句可以在不同的执行语句作用域内进行嵌套,else子句通过{}划分作用域来实现匹配

  • for循环语句的计算顺序:先从左至右执行循环条件语句,如果循环条件语句的判断语句为true,则在循环条件语句执行之后继续执行一次循环执行语句,然后再回到循环条件语句;如果循环语句判断条件为false,则停止循环

  • while和do…while的区别:如果循环条件语句为false,while语句不会执行循环,而do…while保证循环体执行一次
  • 典型循环语句:包括for循环语句,while循环语句和do_while循环语句;for循环语句更多的用于需要遍历的情况,而while和do-while循环语句可以用于状态判断并且根据状态执行操作等
  • break和continue的区别:continue语句只能出现在循环语句中,而break语句可以出现在switch语句中,continue语句不会退出循环,只会终止本次循环并进入下一次循环,break语句会退出循环语句
  • switch语句的执行顺序:如果表达式与其中一个case标号的值匹配,则程序将从该标号后面的第一个语句开始依次执行各个语句,直到switch结束或遇到break语句为止。如果没发现匹配的case 标号,则在存在default语句时执行default语句,否则程序从switch语句后面的第一条语句继续执行。
  • 如何在switch语句定义变量:在switch结构中可以在最后一个case标号或default标号后面定义变量,或者引入块语句来定义变量。
  • 什么是递归:一个过程或函数直接或间接调用自己本身,叫递归过程或递归函数;它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
  • 汉诺塔问题:程序设计中的经典递归问题

  • 实际上,在内存中为每个数据流开辟一个内存缓冲区,是用来存放流中的数据。流是与内存缓冲区相对应的。

  • 什么是标准输入输出流:标准输入流是从标准输入设备(键盘)流向程序的数据;标准输出流是流向标准输出设备(显示器)的数据
  • 在C++语言中,数据的输入和输出包括对标准输入设备键盘和标准输出设备显示器(标准I/O)、对在外存磁盘上的文件(文件I/O)和对内存中指定的字符换存储空间进行输入输出(串I/O)这3个方面。
  • 重载在同一个作用域为一个给定函数名称提供多种定义。编译器根据调用该函数的参量选择合适的函数或运算符版本

文件输入/输出

  • 3种流对象可以用来处理文件:ifstream、ofstream和fstream。
  • 如何读取一个文件:在C++中,读取文件使用iostream头文件内的相关方法。iostream是fstream的基类
  • 文件输入输出有哪几种方式:C++定义了ifstream、ofstream和fstream3种类型以用来支持文件的输入输出这里写图片描述

异常与错误

  • 什么是异常:异常就是程序运行时出现的不正常,它可能会导致系统无法正常运行甚至停止运行等严重情况,编程者需要实现好的异常处理来保证程序的稳定性
  • 如何抛出和捕捉异常:在C++中,系统通过try块和异常处理构成了异常机制:其中通过catch语句来捕捉运行时的异常,并且执行异常处理,通过throw语句可以抛出异常

预处理以及内存管理

3种预处理功能:宏定义、文件包含和条件编译
- 宏定义与操作符的区别:宏定义是C++的预处理命令之一,它是一个替换操作,不做计算和表达式求解,不占用内存和编译时间

# (stringizing)字符串化操作符。其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前

#define  example(str)  #atr
string str = example(abc); ==》 string str = "abc";      

- 宏定义如何展开:用宏定义时设计的宏体去代替宏指令名,并且用实际参数一一取代形式参数

  • include有哪几种使用方式?什么区别?:两种使用方式,#include<>是使用标准头文件,#include“是使用自定义头文件”
  • 包含头文件时如何查找头文件:标准头文件在系统文件目录下查找;用户自定义文件会依次在用户目录,C++安装目录和系统文件中查找
  • 如何分配和释放存储空间:C++中使用new和delete来分配和释放存储空间

虚函数和纯虚函数

  • 虚函数和纯虚函数的区别:纯虚函数是虚函数的一个子集,用于抽象类,含有纯虚函数的类就是抽象类,它不能生成对象
  • 如何使用纯虚函数:纯虚函数用来定义没有意义的实现,用于抽象类中需要交给派生类具体实现的方法

指针

  • 什么是指针:指针是用来存储内存地址的变量,它指向单个对象的地址,除了void指针类型以外,指针的数据类型与所指向地址的变量数据类型必须保持一致
  • 如何初始化指针并对其赋值:指针的初始化就是给指针赋初值,&符号可以用来获取对象的内存地址,并且赋值给指针变量。指针变量的初始化和赋值都可以通过运算符“=”来实现
  • 是否可以确定指针指向一个对象:指针用于指向对象,一个指针只指向一个对象的内存地址
  • 如何使用指针操作数组:C/C++中,指针对于数组的操作是通过将数组的地址,通常是第一个数组元素的地址赋值给指针来进行操作的。
  • const对象的指针和const指针的区别:const指针的值不可以被修改,但是可以使用该指针修改它所指向对象的值;指向const变量的指针不可以修改所指向的const变量的值,但是自身可以被修改。
    不可以使用(void*)指针保存const对象的地址,而必须使用(const void*)类型的指针来保存const对象的地址
  • 数组指针和指针数组的区别:数组指针是一个指针变量,它指向一个数组。而指针数组是一个只包含指针元素的数组,它的元素可以指向相同类型的不同对象。

  • 函数指针以及如何使用:函数指针就是指向函数的存储空间地址的指针。可以对函数指针进行赋值并且通过函数指针来调用函数

  • 指针函数和函数指针的区别:函数指针是一个指向函数的指针,它的本质是一个指针;而指针函数只是说明它是一个返回值为指针的函数,它的本质是一个函数

  • 什么是this指针:在调用成员函数时,编译器会隐含地插入一个参数,这个参数就是this指针。this指针指向当前对象本身,表示当前对象的地址

  • 何时使用this指针:当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,隐含地使用this指针,而通常不去显式地使用this指针来引用数据成员

引用与值传递

  • 什么是值传递:值传递将要传递的值作为一个副本传递,在函数调用时,实参把它的值传递给对应的形参,形参值的改变不影响实参。
  • 引用与值传递的区别:值传递传递的是一个值的副本。函数对形参的操作不会影响实参的值,而引用传递传递的是引用对象的内存地址,函数对形参的操作会影响实参的值,实参的值将会随着形参值得更改而同样进行更改
  • 指针和引用的区别:
    • 指针是一个实体(所指变量的内存地址),而引用仅是一个别名
    • 引用使用时无需解引用,指针需要解引用
    • sizeof引用得到的是所指向的变量(对象)的大小,而指针得到的是指针本身的大小
    • 引用定义时必须要初始化,不能为空,指针可以为空

面向对象与类

  • 面向对象与面向过程的区别:面向过程是一种以过程为中心的编程思想,以算法进行驱动。面向对象是一种以对象为中心的编程思想,以消息进行驱动。面向过程编程语言的组成为:程序=算法+数据,面向对象编程语言的组成为:程序=对象+消息
  • 面向对象的特征:封装(目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是通过外部接口或特定的访问权限使用类的成员)、继承(可以使用现有类的所有功能,在无需重新编写原来的类的情况下对这些功能进行扩展)、多态(根据实际的对象类型决定函数调用的具体目标)。面向对象中所有的对象都可以归属为一个类

类是对某种类型的对象定义变量和方法的原型。它表示对现实生活中一类具有公共特征的事物的抽象。
- C++类和结构的区别:
- C中的结构体和C++中的结构体的不同:C中只能定义数据类型,不允许有函数,而C++中可以定义成员函数,以及public,protected和private数据成员,可以从别的类继承,可以有虚函数
- 不同之处:结构体中默认访问权限是public,而类中的是private
- 抽象类及它的用途:包含纯虚函数的类称为抽象类,抽象类把有共同属性或方法的对象抽象成一个类

类成员

  • 成员变量有哪些访问方式:根据访问权限有private、protected和public3种访问方式
  • 成员变量有哪些访问控制方式:通过声明private、protected和public3种访问权限来对成员变量进行访问控制
  • 如何访问静态成员:使用静态数据成员可以节省内存,因为它是所有对象所共有的;类的静态成员是可以独立访问的,不需要创建类的实例(对象)就可以访问静态成员,也不能用实例(对象)来进行调用。类的静态函数只能调用静态成员,因为静态函数不包含this指针。

多态
多态允许将子类类型的指针赋值给父类类型的指针
- 什么是多态?多态的作用:多态是面向对象编程的核心概念之一,多态技术允许将父类设置成和它的一个或更多的子对象相等
- C++中如何实现多态?:编程者常说的多态指的是动态多态,它是基于继承机制和虚函数来实现的。

继承

  • 派生类和基类的转换:派生类总是可以转换为基类的引用类型(总是合法和自动的)。基类转换为派生类需要在确定安全的清下,使用强制转换来进行转换(基类是不会自动转换为派生类的,因为基类对象只能是基类对象,它不能包含派生类成员)
  • 什么是虚成员?有什么作用:在C++中,虚成员也被称为虚函数,虚函数是用于面向对象中实现多态的机制。它的核心理念就是通过基类访问派生类定义的函数;虚函数的作用是实现动态联编,当程序发现函数名前的关键字virtual后,会自动将其作为动态联编处理,即在程序运行时动态的选择合适的成员函数
  • 构造函数和析构函数的调用时机:构造函数和析构函数的调用时自动进行的。建立对象时会调用构造函数,而销毁对象时调用析构函数

访问控制

继承是指一个对象直接使用另一对象的属性和方法;继承可以使子类具有父类的各种属性和方法
- 有哪几种继承方式:公有继承、保护继承和私有继承
- 继承时访问级别如何变化:
- public(公有继承):基类成员保持自己的访问级别
- protected(保护继承):基类的public和protected成员在派生类中为protected成员;基类的private成员保持为private
- private(私有继承):基类的所有成员在派生类中为private成员

1、覆盖函数和被覆盖函数只有函数体不同,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致
2、覆盖的特征为在不同的范围中(分别位于派生类与基类)的函数,函数名、函数参数,基类必须有virtual关键字
- 什么时候发生函数覆盖:在继承时派生类对于抽象类或者接口声明的方法的具体实现时发生
- 如何访问基类的成员:派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。通过派生类的对象只能访问基类的public成员

复制构造函数

在C++中,类除了提供默认构造函数和析构函数外,编译器还提供一个默认复制构造函数。每当创建对象的复制时,都将调用复制构造函数

  • 深拷贝和潜拷贝:

    • 浅拷贝(内存地址的复制):指的是在对象复制时,只是对对象中的数据成员进行简单的复制,如果对象中存在指针,并且没有自定义拷贝构造函数,使用编译器默认的拷贝构造函数,浅拷贝就会出现问题(释放同一个内存空间2次,导致内存泄漏);
    • 深拷贝(值的拷贝,内存地址不同):针对成员变量存在指针的情况,不仅仅是简单的指针复制,而是重新分配内存空间
  • 什么是拷贝构造函数:

    • 一个对象以值传递的方式传入函数体
    • 一个对象以值传递的方式从函数返回
    • 一个对象需要通过另外一个对象进行初始化

类型转换构造函数

类型转换构造函数可以将其他类型直接转换为类的对象。
- 类型转换分类:隐式转换和显式转换
- 什么是类型转换构造函数:一个类的构造函数只有一个参数,而且这个参数不是该类的类型而是其他类型

函数

  • 什么是函数:函数由函数名、参数、返回值类型以及一组包含操作语句的语句块组成。函数支持重载,程序是由函数组成的
  • 形参和实参区别:形参是函数定义或声明时的函数形式参数,形参表制定了函数参数的个数和数据类型,实参是函数调用时传递给函数的参数,传递时要与形参一一对应
  • C++支持参数个数不确定的函数吗:C++可以通过隐藏参数机制来支持参数个数不确定的函数
  • 什么是内联函数:在类声明的内部声明或定义的成员函数叫做内联函数。引入内联函数的目的是解决函数调用的效率问题。一般来说,内联机制适用于只有几行的小的而且经常被调用的函数

函数参数的传递

  • 引用形参和非引用形参的区别:把引用变量当成形参传递就是引用形参,由于引用变量指向的是同一个内存地址,实际上传递的是变量的地址;引用形参把对变量的操作,隐式地转成通过变量地址来对变量操作
  • 使用引用形参的问题: 调用非const类型的引用形参,实参必须不是const类型,而且实参的类型和形参的类型应当一致;调用一个const引用的形参的函数时,如果实参不是一个变量或者类型不匹配时,函数会创建一个无名的临时变量用来存储实参的值,并把这个形参作为该临时变量的引用
  • 指针形参和引用形参的区别:函数的形参可以是指针,此时将复制实参指针。与其他非引用类型的形参一样,该类形参的任何改变仅作用于局部副本,主调函数使用的实参指针的值没有改变;引用形参实际传递的是变量的地址,主调函数的实参的值会跟随被调函数引用形参的改变而改变

类成员函数

  • 什么是类成员函数?有哪些特别的类成员函数:类成员中的函数就是类的成员函数;构造函数和析构函数
  • 什么是静态函数?如何使用:静态函数是使用static修饰符修饰的函数,静态函数没有this指针,只能访问静态变量。在类中如果函数调用的结果不会访问或者修改任何对象数据成员,这样的成员声明为静态成员函数比较好
  • 静态函数能访问类的私有成员:静态函数只能直接访问类的静态私有成员,静态函数不可以直接访问类的非静态私有成员
  • 一个类可以访问另一个类的私有成员吗:类的私有成员是不可以被外部类访问的,但是外部类可以使用宏定义等特殊方法来实现访问类的私有成员

函数重载

  • 函数重载与作用域:函数重载是指在相同的作用域中,具有相同函数名而形参列表不同的多个函数。
    • 如果局部地声明一个函数,则该函数将屏蔽而不是重载在外层作用域中声明的同名函数
  • 函数重载的匹配:编译器遇到对重载函数的调用时,必须确定调用哪个函数。如果没能找到参数完全匹配的函数,则找一个替代函数。此时编译器将函数调用的实参与所有重载函数的参数比较,这一过程成为参数匹配
  • 函数重载时如何实现实参的类型转换:在函数重载匹配时,先通过标准转换来实现匹配,如果不行,再通过类类型转换来实现匹配

模板和STL

  • 什么是函数模板:是使用模板技术定义了参数化类型的非成员函数,这使得程序能够使用不同的参数类型调用相同的函数
  • 什么是类模板:类模板是使用模板技术的类,描述了能够管理其他数据类型的通用数据类型。类模板技术通常用于建立包含其他类型的容器类,例如队列、链表和堆栈等
    • 调用时需要明确指出使用何种数据类型,而不能由编译器自行指定

容器

  • 什么是容器:容器是容纳特定类型对象的集合,在STL中,有顺序容器和关联容器
    • 顺序容器:指的是将一组相同类型T的对象,以严格的线性形式组织在一起;顺序容器可以视为数组和链表的扩展:vector、deque、list
      • vector是一种顺序容器,存放的元素是以连续的数组的方式存储的,可以通过v[i]的方式访问vector里面的元素;对特定值的访问,只能通过遍历vector的方式进行比较,对vector插入或者删除元素时,需要保持元素的连续性,相对来说性能较低
      • vector是一种容器,那么可以向vector’添加一个数据。push_back():表示将数据添加到vector尾部,并按需分配内存
      • 访问vector中的数据有如下两种方法:vector::at()和vector::operator[];at()是首选,at()进行了边界检查,如果访问超过了vector的范围,将抛出一个异常,operator[]则不会
    • deque容器是一个双端队列,存放的数据不是以连续的形式存放的
    • list容器:一种链表的实现,存储的元素是通过使用双向链表实现的;其优势在于在list的任意位置插入和删除,非常快速。但是list在查找和随机存取时需要更大的开销
  • 什么是迭代器的范围(迭代器是STL提供的对一个容器中对象的访问方法):迭代器有两个可以通过容器的begin()操作和end()操作获取。其中begin指向容器中的第一个元素,end指向的是容器中的最后一个元素的下一个位置,也就是说end所指向的并不是容器的元素。通常begin和end之间的范围就是迭代器的范围

    使用迭代器对容器进行遍历
    vector<int> v;
    v.push_back(2);
    v.push_back(1);
    vector<int>::iterator first = v.begin();
    while(first != v.end())
    {
        int  i = *first;
        first++;
    

    }

  • 什么是关联容器:能够通过键值(关键字)来查找和读取元素的容器。在STL中有4个关联容器:map、set、multimap和multiset容器

    • map的元素是“键-值”对的二元组形式即键用作元素在map中的索引,值表示所存储和读取的数据。set和map类型的对象所包含的元素具有不同的键,如果需要一个键对应多个实例,则需要使用multimap或multimap类型
    • set是一个容器,它其中所包含的元素的值是唯一的。集合中的元素按一定的顺序排列,并被作为集合中的实例。一个集合通过一个链表来组织,在插入操作和删除操作上比vector快,但查找或添加末尾的元素时会有些慢

泛型编程

  • 什么是泛型编程:以独立于(不考虑)任何特定类型的编程方式,针对不同的类型提供通用的实现
  • C++如何实现泛型编程:使用C++中的模板技术来实现的,主要包括函数模板和类模板
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章