C++ 面试问题与答案 (2025)
C+±-众所周知的、程序员最喜爱的编程语言。 它在上世纪 80 年代中期仍具有重要意义。 作为一种通用和面向对象的编程语言,C++ 在编码过程中被广泛使用。 因此,一些工作岗位要求个人精通 C++。 由于其可靠性、性能和广泛的使用场合,Evernote、LinkedIn、微软、Opera、NASA 和 Meta 等顶级 IT 公司都在使用 C++。 因此,要想进入这些公司,你需要全面掌握这 50 个 C++ 面试问题,这能让你在招聘人员面前显得像个专家。
为了让您做好面试准备,我为初学者、中级者和有经验者提供了 50 大 C++ 面试问题,您一定要仔细阅读,以便让自己进入顶级跨国公司。
面向应届生的 C++ 面试问题
1. 什么是 C++? C++ 有哪些优点?
C++ 是一种面向对象的编程语言,它的出现是为了克服 C 语言的不足。 所谓面向对象,是指它使用多态、继承、抽象、封装、对象和类等概念。
C++ 的优势:
- C++ 是一种 OOPs 语言,这意味着数据被视为对象。
- C++ 是一种多范式语言;简单地说,它意味着我们可以对程序的逻辑、结构和过程进行编程。
- 内存管理是 C++ 的一个关键功能,因为它可以实现动态内存分配
- 它是一种中级编程语言,这意味着它可以开发游戏、桌面应用程序、驱动程序和内核
2. C++ 中有哪些不同的数据类型?
以下是 C++ 中的数据类型列表:
3. 定义 “std”?
std “也被称为 “标准”,也可以理解为一个命名空间。 using namespace std”(使用命名空间 std)命令通知编译器将 std 命名空间下的所有内容添加到全局命名空间中。 这种全局命名空间的灌输使我们可以在不使用 "std::operator"的情况下使用 "cout "和 “cin”。
4. 什么是 C++ 中的引用?
在 C++ 中,引用是为另一个变量创建别名的另一种方法。 引用作为变量的同义词,允许你直接访问该变量,而不需要任何额外的语法。 创建引用时,必须对其进行初始化,之后就不能再将其更改为引用另一个变量。 这一特性使得在函数中操作变量变得更容易,同时避免了复制大型对象的开销。 引用变量前面有一个"&"符号。
语法
int GFG = 10;
// reference variable
int& ref = GFG;
5. 什么是 "按值调用 "和 “按引用调用”?
在编程语言中,调用函数有两种方法: 通过值调用和通过引用调用
按值调用 | 按引用调用 |
---|---|
传递变量的副本。 | 变量本身是从根本上传递的。 |
通过复制变量发送值来调用函数。 | 通过发送所传递变量的地址来调用函数。 |
在函数中所做的更改永远不会反映在函数外部的变量上。简而言之,在 Call by Value 中永远不会更改原始值。 | 在函数中所做的更改可以在函数外部的传递函数上看到。简而言之,原始值在 Call by reference 中被更改。 |
传递的 actual 和 formal 参数存储在不同的内存位置。因此,使 Call by Value 内存不足 | 传递的 actual 和 formal 参数存储在同一个内存位置。因此,使 Call by Reference 的内存效率更高一些。 |
6. 在 C++ 中定义标记
标记是编译器能理解的程序中最小的单个元素。 标记包括以下内容:
- 关键字–对编译器具有特殊意义
- 标识符–具有唯一的值/标识
- 常量 - 在整个程序中其值永不改变
- 字符串 - 包含相同的数据序列
- 特殊符号 - 它们具有某种特殊含义,不能用于其他目的;例如:[] () {}, ; * = #
- 操作符 - 对操作数进行操作的符号
7. C 和 C++ 有什么区别?
下表列出了 C 和 C++ 的主要区别:
C | C++ |
---|---|
它是一种程序化编程语言。 简单地说,它不支持类和对象 | 它是程序设计语言和面向对象程序设计语言的混合体。 简单地说,它支持类和对象。 |
它不支持任何 OOPs 概念,如多态性、数据抽象、封装、类和对象。 | 它支持所有数据概念 |
它不支持函数和操作符重载 | 它分别支持函数和操作符重载 |
它是一种函数驱动语言 | 它是一种对象驱动语言 |
8. 结构体和类有什么区别?
下表列出了结构体和类的主要区别:
方面 | 结构体 | 类 |
---|---|---|
默认访问修饰符 | 成员默认为公共成员。 | 成员默认为私有。 |
内存分配 | 可在堆栈或堆上分配。 | 可在堆栈或堆上分配。 |
继承 | 支持继承(具有公共、受保护或私有访问权限)。 | 支持继承(公共、受保护或私有访问)。 |
用例 | 常用于旧数据(POD)结构或简单的数据分组。 | 适用于可能包含方法、构造函数和析构函数的复杂对象。 |
9. 引用和指针有什么区别?
引用和指针的主要区别如下:
引用 | 指针 |
---|---|
引用的值不能重新分配 | 指针的值可以重新分配 |
指针永远不能持有空值,因为它需要一个现有的值作为其别名 | 它可以持有或指向空值,因此被称为 nullptr 或 null 指针 |
访问类/结构体的成员时,使用". " | 访问类/结构体的成员时使用"->"。 |
引用的内存位置可以很容易地访问,也可以直接使用 | 指针的内存位置不能很容易地访问,因为我们必须使用反引用 “*”。 |
10. 函数重载和操作符重载有什么区别?
操作符重载和函数重载的主要区别如下:
函数重载 | 操作符重载 |
---|---|
它基本上是以多种方式定义一个函数,从而有多种方式调用该函数,简单地说,就是同一函数有多个版本 | 它基本上是对运算符的现有含义赋予特殊含义的做法,简单地说,就是重新定义预先定义的含义。 |
参数化函数是函数重载的一个很好的例子,因为只要改变函数的参数,就能使其用于不同的目的 | 多态性是运算符重载的一个很好的例子,因为分配类的对象可以被不同的类用于不同的目的并调用 |
函数重载示例:1. int GFG(int X, int Y);2.int GFG(char X, char Y); | 操作符重载示例:1.int GFG() = X() + Y();2.int GFG() = X() - Y(); |
11. 数组和列表有什么区别?
数组和列表的主要区别在于:
数组 | 列表 |
---|---|
数组是以固定位置或大小存储的同源数据类型的连续内存位置。 | 列表是通过指针相互链接或连接的经典单个元素,没有固定大小。 |
数组是静态的。 | 列表是动态的 |
比链接列表占用更少内存。 | 由于需要存储值和指针的内存位置,因此使用的内存较多。 |
12. while 循环和 do-while 循环有什么区别?
while 循环和 do-while 循环的主要区别如下:
While 循环 | do-while 循环 |
---|---|
While 循环也称为入口控制循环 | do-while 循环称为出口控制循环 |
如果条件未满足,循环内部的语句将不执行 | 即使条件未满足,循环内部的语句也将至少执行一次 |
13. 讨论前缀和后缀的区别?
前缀和后缀的主要区别如下:
前缀 | 后缀 |
---|---|
简单地说,就是把运算符放在操作数之前 | 简单地说,就是把运算符放在操作数之后 |
在"; "之前执行自身运算 | 在"; "之后执行自身运算 |
前缀 ++ 的关联性从右向左 | 后缀 ++ 的关联性从左向右 |
14. new 和 malloc() 有什么区别?
new 和 malloc() 的主要区别如下:
new | malloc() |
---|---|
new 是执行操作的操作符 | malloc 是返回和接受值的函数 |
new 调用构造函数 | malloc 不能调用构造函数 |
new 比 malloc 快,因为它是一个运算符 | malloc 比 new 慢,因为它是一个函数 |
new 返回精确的数据类型 | malloc 返回 void* |
15. 虚拟函数和纯虚拟函数有什么区别?
虚拟函数和纯虚拟函数的主要区别如下:
虚拟功能 | 纯虚拟函数 |
---|---|
虚拟函数是基类的成员函数,可以在另一个派生类中重新定义。 | 纯虚拟函数是基类的成员函数,只在基类中声明,并在派生类中定义,以防止成为抽象类。 |
虚拟函数的定义在其各自的基类中。 | 纯虚函数(Pure Virtual Function)没有定义,初始化时使用纯指符(= 0)。 |
基类有一个虚拟函数,可以被表示或实例化;简单地说,它的对象可以被制造出来。 | 具有纯虚拟功能的基类成为抽象类,不能被表示或实例化;简单地说,这意味着它的对象不能被制造出来 |
16. 什么是 C++ 中的类和对象?
类是一种用户定义的数据类型,其中的所有成员函数和数据成员都是根据需求和要求定制的,此外还可以借助对象来访问这些成员函数和数据成员。 我们使用关键字 class 来声明用户定义的数据类型。
对象是一个类的实例,是具有值和状态的实体;简单地说,它被用作催化剂或代表一个类的成员。 它可能包含不同的参数,也可能不包含任何参数。
注:类是定义对象所用功能的蓝图。
17. 什么是函数覆盖?
当基类中已经存在/声明的同名函数、相同参数和相同返回类型被派生类使用时,就称为函数重载。 它是运行时多态性(Runtime Polymorphism)或后期绑定(Late Binding)的一个例子,这意味着重载函数将在运行时执行。
18. C++ 中有哪些 OOPs 概念?
以下是 C++ 中的 OOPs 概念:
- 类: 它是用户定义的数据类型
- 对象: 类的实例
- 抽象: 这是一种只显示必要细节的技术
- 封装: 将数据封装在一个单元中
- 继承: 一个类从另一个类派生属性和特征的能力
- 多态性: 多态性被称为同一事物的多种形式
19. 解释继承
一个类从另一个类中派生属性和特征的能力称为继承。 简单地说,它是一种在不修改现有类的情况下重复使用和扩展这些类的系统或技术。
20. 什么情况下应使用多重继承?
多重继承是指一个派生类可以继承两个或多个基类/父类。 当派生类需要结合许多属性/契约,并从这些属性/契约继承部分或全部实现时,多重继承就非常有用。 举个现实生活中的例子,父母 A 是你的爸爸,父母 B 是你的妈妈,孩子 C 是你。
21. 什么是虚拟继承?
虚拟继承是一种确保子类派生类只继承一个基类成员变量副本的技术。 简单地说,当我们处理多重继承的情况,但又想防止继承层次结构中出现同一类的多个实例时,就会使用虚拟继承。
22. 什么是 C++ 中的多态性?
多态被称为同一事物的多种形式。 简单地说,多态性就是根据调用成员函数的对象类型,以多种形式显示成员函数的能力。
换句话说,我们也可以说一个人可以是某人的雇员、某人的儿子、某人的父亲和某人的丈夫;这就是多态性在现实生活中的体现。
多态性有两种类型:
- 编译时多态性
- 函数重载
- 操作符重载
- 运行时多态性
- 函数重载
- 虚拟函数
23. C++ 有哪些不同类型的多态性?
有两种多态性
编译时多态或静态绑定
这种类型的多态性是在程序编译时实现的,因此它比运行时要快一些。 此外,它不涉及继承。 它还包括两种技术:
函数重载: 当多个函数名称相同但参数不同时,这就是所谓的函数重载。
// same name different arguments
int GFG() {}
int GFG(int a) {}
float GFG(double a) {}
int GFG(int a, double b) {}
操作员超载: 操作符重载基本上是对操作符的现有含义赋予特殊含义的做法,简单地说,就是重新定义预先定义的含义。
class GFG {// private and other modesstatements public returnTypeoperator symbol(arguments){ statements } statements
};
运行时多态性或后期绑定
运行时多态性发生在运行时调用函数时。
函数重载: 当基类成员函数在派生类中以相同的参数和返回类型被重新定义时,就发生了函数重载。
// C++ program to demonstrate
// Function overriding
#include
<iostream> using namespace std;
class GFG {
public:virtual void display(){cout << "Function of base class" << endl;}
};
class derived_GFG : public GFG {
public:void display(){cout << "Function of derived class" << endl;}
};
int main()
{derived_GFG dg;dg.display();return 0;
}
输出:
Function of derived class
24. 比较编译时多态性和运行时多态性
以下是运行时多态性与编译时多态性的主要区别:
编译时多态性 | 运行时多态性 |
---|---|
也称为静态绑定和早期绑定。 | 也称为动态绑定和后期绑定。 |
它速度快,因为在编译时就知道执行情况。 | 与编译时相比,运行时多态性速度较慢,因为运行时才知道执行情况。 |
通过函数重载和操作符重载实现。 | 通过虚拟函数和函数重载实现。 |
25. 解释 C++ 中的构造函数。
构造函数是类的一种特殊成员函数,其名称与调用它的类的名称相同,并为类的对象初始化值。
构造函数有三种类型:
A. 默认构造函数: 这是最基本的构造函数类型,不接受任何参数。 即使不调用它,编译器也会在创建对象时自动调用。
例如:
class Class_name {
public:Class_name() { cout << "I am a default constructor"; }
};
B. 参数化构造函数: 这是一种接受参数的构造函数。 它必须通过在参数中传递值来明确调用,因为这些参数有助于在创建对象时对其进行初始化。 它的名称也与类的名称相同。
此外,它还用于重载构造函数。
例如:
// CPP program to demonstrate
// parameterized constructors
#include
<iostream> using namespace std;
class GFG {
private:int x, y;public:// Parameterized ConstructorGFG(int x1, int y1){x = x1;y = y1;}int getX() { return x; }int getY() { return y; }
};
int main()
{// Constructor calledGFG G(10, 15);// Access values assigned by constructorcout << "G.x = " << G.getX() << ", G.y = " << G.getY();return 0;
}
输出
G.x = 10,G.y = 15
C. 复制构造函数 复制构造函数是一个成员函数,它使用同类的另一个对象来初始化一个对象。 此外,复制构造函数将同类对象的引用作为参数。
例如:
Sample(Sample& t) { id = t.id; }
26. 什么是 C++ 中的析构函数?
析构函数是类中函数的成员,当类中的对象退出作用域时,析构函数会删除对象。 析构函数的名称与类的名称相同,并在其前面加上一个"~"符号。 此外,与构造函数从上到下的方式不同,析构函数采用从下到上的方式。
语法:
~constructor_name(); // tilde sign signifies that it is a destructor
27. 什么是虚拟析构函数?
当使用基类指针对象销毁派生类的实例或对象时,会调用虚拟析构函数来释放派生类对象或实例分配的内存空间。
虚拟析构函数保证首先调用派生类的析构函数。 然后调用基类的析构函数,释放继承类中两个析构函数占用的空间,从而避免内存泄漏。 建议只要类是多态的,就将析构函数设置为虚拟的。
28. 析构函数是否可以重载? 如果可以,请解释;如果不可以,为什么?
答案很简单:不,我们不能重载析构函数。 在 C++ 中,每个类只能有一个析构函数。 此外,析构函数既不接受参数,也没有有助于重载的参数。
C++ 面试问题 - 中级水平
29. 指针允许进行哪些操作?
指针是用于存储另一个变量地址位置的变量。 允许对指针进行的操作有
- 指针的增减
- 指针的整数加法和减法
- 比较同一类型的指针
30. "delete"操作符的作用是什么?
删除操作符用于通过取消分配内存来删除/移除对象的所有特征/属性;此外,它最终返回 true 或 false。 简单地说,它可以销毁或取消分配由新表达式创建的数组和非数组(指针)对象。
int GFG = new int[100];
// uses GFG for deletion
delete[] GFG;
31. delete [] 与delete有何不同?
delete[] | delete |
---|---|
用于删除整个数组 | 仅用于删除一个指针 |
用于删除 new[] 的对象;因此,我们可以说 delete[] 用于删除对象数组 | 用于删除 new 的对象;因此,我们可以说 delete 用于删除单个对象 |
它可以调用任意多个析构函数 | 它只能调用一次类的析构函数 |
32. 你对 friend 类和 friend 函数了解多少?
友类是指可以访问被声明为友类的受保护变量和私有变量的类。
友类示例:
class Class_1st {// ClassB is a friend class of ClassAfriend class Class_2nd;statements;
} class Class_2nd {statements;
}
友函数是用于访问其他类的私有、受保护和公共数据成员或成员函数的函数。 它使用 friend 关键字声明。 友函数的优点是不受类的范围限制,而且一旦在类中声明,类的对象就不能调用它,因此它可以被其他函数调用。 综上所述,我们可以说友人函数是一个全局函数。
友函数示例:
class GFG {statements;friend dataype function_Name(arguments);statements;
} OR class GFG {statements' friend int divide(10, 5);statements;
}
33. 什么是溢出错误?
当数据类型无法处理的数字过大时,就会出现溢出错误。 简单地说,它是一种对定义有效但超出定义范围的错误。
例如,int 数据类型的范围是-2,147,483,648 到 2,147,483,647,如果我们声明一个大小为 2,247,483,648 的变量,就会产生溢出错误。
34. 作用域解析操作符的作用是什么?
作用域解析运算符用":: "符号表示。 正如它的名字一样,该操作符用于解决程序中的作用域障碍。 作用域解析运算符用于引用超出其作用域的成员函数或全局变量,此外,它还可以访问程序中隐藏的变量或函数。
范围解析用于大量任务:
- 在有同名局部变量的情况下访问全局变量
- 在类外定义函数
- 多重继承时
- 命名空间
35. 什么是 C++ 访问修饰符?
对类成员(无论是成员函数还是数据成员)的访问限制称为访问修饰符/指定符。
访问修饰符有三种类型:
- 私有(Private)- 它既不能被访问,也不能被类外的人查看。
- 受保护 - 只有在访问者是派生类的情况下才能访问
- 公共 - 可以从类外访问或查看
36. 可以编译没有 main 函数的程序吗?
是的,编译程序时完全可以不使用 main()。 例如 使用定义了 main() 的宏来编译程序。
// C++ program to demonstrate the
// a program without main()
#include
<stdio.h>
#define fun mainint fun(void)
{printf("Geeksforgeeks");return 0;
}
37. 什么是 STL?
STL 全称为标准模板库,是一个提供容器、算法、迭代器和函数对象 4 个组件的库。
38. 定义内联函数。 我们能否在 C++ 中使用递归内联函数?
内联函数是一种请求形式,而不是向编译器发出的命令,其结果是将我们的函数内联到主函数体中。 如果函数的执行时间小于从调用函数到被调用函数的切换时间,内联函数就会成为开销函数。 要使函数内联,请在调用函数之前使用关键字 inline before 并定义函数。
语法:
inline data_type function_name()
{
Body;
}
答案是否定的;它不能递归。
内联函数不能递归,因为在内联函数中,代码只是被放置到调用它的位置,而不会在堆栈中保留递归所需的信息。
此外,如果在递归函数前面写入内联关键字,编译器会自动忽略它,因为内联只是编译器的一个建议。
39. 什么是抽象类,何时使用?
抽象类是一个专门设计用作基类的类。 抽象类至少包含一个纯虚函数。 在类声明中的虚拟成员函数的声明中使用 pure 指定符(= 0)来声明纯虚拟函数。
不能将抽象类用作参数类型、函数返回类型或显式转换类型,也不能声明抽象类的对象。 不过,可以用它来声明指向抽象类的指针和引用。
如果想在组件的所有实现中提供一个通用的、已实现的功能,就可以使用抽象类。 抽象类允许您部分地实现您的类,而接口则没有任何成员的实现。 简单地说,如果你想为你的子类提供实现细节,但又不想让你的类的实例被直接实例化,那么抽象类就非常适合。
40. 什么是静态数据成员和静态成员函数?
类的静态数据成员是一个普通的数据成员,但前面有一个 static 关键字。 它在程序中的 main() 之前执行,并在类的第一个对象创建时初始化为 0。 它只对已定义的类可见,但其作用域是有生命周期的。
语法:
static Data_Type Data_Member;
静态成员函数是用于访问其他静态数据成员或其他静态成员函数的成员函数。 它也是用 static 关键字定义的。 我们可以使用类名或类对象访问静态成员函数。
语法:
classname::function name(parameter);
C++ 面试问题 - 专家级
41. 关键字 "Volatile "的主要用途是什么?
就像它的名字一样,事情可能会在毫无预料的情况下突然发生变化;因此它用于告知编译器值可能随时发生变化。 此外,volatile 关键字还能防止编译器对代码进行优化。 volatile关键字用于与内存映射硬件、信号处理器和机器码指令连接。
42. 在 C++ 中定义存储类并命名一些存储类
存储类用于定义变量或函数的特征(生命周期和可见性)。 这些特征通常有助于在程序运行期间追踪变量的存在。
语法:
storage_class var_data_type var_name;
某些类型的存储类:
43. 什么是可变存储类规范? 如何使用?
就像它的名字一样,可变存储类指定符仅用于类数据成员,使其可以修改,即使该成员是声明为常量的对象的一部分。 静态、常量或引用成员不能使用可变规范。 当我们将一个函数声明为 const 时,传递给函数的指针就会变成 const。
44. 定义块作用域变量
变量的作用域是变量可访问的区域。 有两种作用域:全局和块或局部。
块作用域变量也称为局部变量。 在函数(如 main)或代码块(如循环和 if 代码块)中定义的变量是局部变量。 它只能在声明它的特定函数/代码块中使用。即使代码块位于函数内部,代码块作用域变量也不能在代码块之外使用。
45. 关键字 "Auto "的作用是什么?
auto 关键字可用于直接声明类型复杂的变量。 如果初始化短语包含模板、函数指针、成员引用等,则可以使用 auto 声明变量。 有了类型推断功能,我们就可以花更少的时间写出编译器已经知道的东西。 由于所有类型都是在编译阶段推导出来的,因此编译时间会略有增加,但不会影响程序的运行时间。
46. 在 C++ 中定义命名空间。
命名空间使我们能够将原本具有全局作用域的命名项组织成较小的作用域,从而赋予它们命名空间作用域。 这样,程序各部分就可以被组织成具有名称的不同逻辑作用域。 命名空间为定义或声明变量、方法和类等标识符提供了场所。
或者我们也可以说,命名空间是一个声明性区域,它赋予其中的标识符(类型、函数、变量等的名称)一个作用域。 命名空间用于将代码按逻辑类别排列,避免名称冲突,当你的代码库中有许多库时,可能会发生名称冲突。
47. 何时使用 void() 返回类型?
void 关键字用作函数返回类型时,表示函数不返回值。 void 用作函数的参数列表时,表示函数不带参数。 不返回值的函数也称为 void 函数。 它们之所以被称为 “void”,是因为它们不返回任何值。 没错,但只是部分原因。 我们不能从 void 函数中返回值,但肯定可以返回一些东西。 虽然 void 函数没有返回类型,但它们可以返回值。
48. 浅拷贝和深拷贝有什么区别?
以下是浅复制与深复制的主要区别:
浅层复制 | 深度复制 |
---|---|
在浅层拷贝中,存储的是原始对象的副本,最后只拷贝引用地址。 简单地说,浅层复制就是尽可能少地复制 | 在深度复制中,原始对象的副本和重复副本都被存储。 简单地说,深度复制就是复制所有内容 |
集合的浅层拷贝是集合结构的拷贝,而不是元素的拷贝。 通过浅层拷贝,两个集合现在共享单个元素。 | 集合的深度副本是两个集合,其中复制了原始集合中的所有元素。 |
浅拷贝速度更快 | 深拷贝速度相对较慢。 |
49. 我们能从构造函数中调用虚拟函数吗?
可以,我们可以从构造函数中调用虚拟函数。 但可能会出现重载异常。
50. 什么是 void 指针?
正如它的名字一样,void 指针是一个不与任何事物或任何数据类型相关联的指针。 不过,void 指针可以保存任何类型的地址值,并可以从一种数据类型转换为另一种数据类型。
附加问题:
什么是 C++ 中的 "this "指针?
this 指针使每个对象都能通过一个基本指针访问自己的地址。 所有成员函数都将该指针作为隐式参数。该指针可用于指代成员函数中的调用对象。
- 该指针用于将一个对象作为参数传递给另一个方法。
- 每个对象都有自己的数据成员副本。
- 该指针用于声明索引器。