1.c++面试题
C++ 面试题可以涵盖多个方面,从基础语法到高级特性,再到实际应用和性能优化等。以下是一些可能的 C++ 面试题:
-
基础语法和特性:
- 简述 C++ 的基本数据类型和它们的存储大小(如 int, char, float, double 等)。
- 解释一下指针和引用的区别。
- 什么时候使用
const
关键字?它有什么作用? - 什么是 RAII(Resource Acquisition Is Initialization)?它在 C++ 中是如何应用的?
- 描述一下 C++ 中的函数重载和函数覆盖(虚函数)。
- 简述 C++ 中的拷贝构造函数和赋值操作符重载的作用。
-
面向对象编程:
- 解释一下类、对象、封装、继承和多态的概念。
- 什么时候应该使用虚析构函数?
- 什么是纯虚函数和抽象类?它们有什么用?
- 描述一下 C++ 中的 RTTI(运行时类型识别)和它的应用。
-
内存管理:
- 解释一下堆内存和栈内存的区别。
- 描述一下 C++ 中的内存泄漏以及如何避免它。
- 什么是智能指针(如 std::unique_ptr, std::shared_ptr, std::weak_ptr)?它们之间有什么区别?
-
STL(标准模板库):
- 解释一下 STL 中的迭代器、容器(如 vector, list, map 等)和算法。
- 如何在 STL 容器中使用自定义类型?
- 描述一下 STL 中的 allocator 的作用。
-
模板和元编程:
- 解释一下模板的概念和它的作用。
- 描述一下函数模板和类模板的区别。
- 解释一下模板特化和偏特化的概念。
- 什么是元编程?它在 C++ 中是如何应用的?
-
异常处理和错误处理:
- 解释一下 C++ 中的异常处理机制(try-catch-throw)。
- 如何在 C++ 中进行错误处理?
-
多线程和并发编程:
- 解释一下 C++11 中的多线程支持(如 std::thread, std::mutex, std::condition_variable 等)。
- 什么是线程安全和可重入函数?
- 如何使用 C++ 实现生产者-消费者模型?
-
性能优化和调试:
- 描述一下你在 C++ 中进行性能优化的经验。
- 解释一下 RAII 和性能优化的关系。
- 如何使用调试工具(如 gdb, valgrind 等)来调试 C++ 程序?
-
系统编程和底层编程:
- 解释一下 C++ 在系统编程中的应用(如操作系统、网络编程等)。
- 如何使用 C++ 进行底层编程(如内存管理、寄存器操作等)?
-
设计模式和软件设计原则:
- 描述一下你熟悉的设计模式(如单例模式、工厂模式、观察者模式等)和它们的应用场景。
- 解释一下 SOLID 设计原则的内容和应用。
以上只是一些可能的 C++ 面试题,实际面试中可能会根据应聘者的经验和职位需求进行选择和调整。
2.std::unique_ptr, std::shared_ptr, std::weak_ptr 的中文名分别叫啥
std::unique_ptr
、std::shared_ptr
和 std::weak_ptr
在中文中通常被翻译为:
std::unique_ptr
:唯一指针(或独占指针)std::shared_ptr
:共享指针std::weak_ptr
:弱指针
这些智能指针是 C++11 标准库 <memory>
头文件中提供的,用于管理动态分配的内存,并自动处理对象的生命周期,从而避免内存泄漏和野指针问题。
std::unique_ptr
是一种独占所有权的智能指针,它确保同一时间只有一个unique_ptr
指向某个对象,当unique_ptr
被销毁时,它所指向的对象也会被自动删除。std::shared_ptr
允许多个shared_ptr
指向同一个对象,对象的生命周期会在最后一个拥有它的shared_ptr
被销毁时结束。这是通过引用计数实现的。std::weak_ptr
是对shared_ptr
的一种补充,它持有对对象的弱引用,即不增加对象的引用计数。它主要用于解决shared_ptr
之间的循环引用问题。
3.c++ 初始化列表
在C++中,初始化列表(Initializer List)是一个在构造函数中用于初始化成员变量的机制。当创建类的对象时,构造函数的初始化列表用于在构造函数体执行之前初始化成员变量。使用初始化列表通常比在构造函数体内赋值更高效,因为成员变量可以在对象真正构造之前就被设置为其初始值。
初始化列表的语法是在构造函数的冒号(:
)后面列出成员变量和它们的初始值。下面是一个简单的例子:
class MyClass {
public:
int x;
std::string str;
MyClass(int value, const std::string& text)
: x(value), // 使用初始化列表初始化成员变量x
str(text) // 使用初始化列表初始化成员变量str
{
// 构造函数体为空,因为所有成员变量都已经在初始化列表中初始化了
}
};
int main() {
MyClass obj(10, "Hello, World!"); // 使用初始化列表初始化MyClass对象
return 0;
}
在上面的例子中,MyClass
的构造函数使用了初始化列表来初始化成员变量 x
和 str
。
初始化列表特别适用于以下几种情况:
- 常量成员:常量成员变量只能在初始化列表中初始化,因为它们不能在构造函数体内被赋值。
class ConstMemberClass {
public:
const int constVal;
ConstMemberClass(int val) : constVal(val) {} // 必须在初始化列表中初始化
};
- 引用成员:引用成员也必须在初始化列表中初始化。
class RefMemberClass {
public:
int& refVal;
RefMemberClass(int& value) : refVal(value) {} // 必须在初始化列表中初始化
};
- 基类:如果类有基类,基类的构造函数也需要在初始化列表中调用。
class Base {
public:
Base(int val) { /* ... */ }
};
class Derived : public Base {
public:
Derived(int val) : Base(val) {} // 调用基类的构造函数
};
- 防止不必要的默认初始化:对于自定义类型的成员变量,使用初始化列表可以避免不必要的默认初始化后再赋值。
注意:并不是所有成员变量都必须在初始化列表中初始化。如果某个成员变量没有在初始化列表中初始化,它将在构造函数体执行之前使用其类型的默认构造函数进行初始化(如果可用)。如果成员变量没有默认构造函数,且没有在初始化列表中初始化,那么编译器将报错。
4.初始化列表与初始化顺序的关系
在C++中,成员变量的初始化顺序与它们在类定义中的声明顺序相同,而不是与它们在初始化列表中的出现顺序相同。这意味着,即使你在初始化列表中首先列出了某个成员变量,它的初始化也会在其后声明的成员变量之后发生。
这是一个重要的规则,因为如果成员变量的初始化依赖于其他成员变量的已初始化状态,而你又错误地假设了初始化顺序,那么你的程序可能会出现未定义的行为。
以下是一个示例,说明了初始化顺序与声明顺序的关系:
class MyClass {
public:
int a; // 首先声明
int b; // 然后声明
MyClass(int valueA, int valueB)
: b(valueB * a), // 这里尝试使用a的值,但此时a尚未初始化
a(valueA) // a的初始化在这里
{
// 构造函数体
}
};
int main() {
MyClass obj(10, 2); // 这里会导致未定义行为,因为b在a之前尝试初始化
return 0;
}
在上面的代码中,尽管在初始化列表中b
被列在a
之前,但a
会在b
之前被初始化。因此,当尝试计算b
的初始化值(valueB * a
)时,a
的值尚未被设置,这会导致未定义的行为。
正确的初始化顺序应该与声明顺序一致:
class MyClass {
public:
int a; // 首先声明
int b; // 然后声明
MyClass(int valueA, int valueB)
: a(valueA), // 先初始化a
b(valueB * a) // 然后使用已初始化的a来初始化b
{
// 构造函数体
}
};
int main() {
MyClass obj(10, 2); // 现在一切正常,因为b在a之后初始化
return 0;
}
在这个修改后的版本中,a
在b
之前被初始化,因此b
可以使用a
的已初始化值来进行初始化。
5.c++成员变量的初始化和构造函数那个先运行
在C++中,当创建类的对象时,成员变量的初始化和构造函数的执行有一定的顺序,但“成员变量的初始化”通常指的是在构造函数体内的赋值操作和在构造函数初始化列表中进行的初始化。实际上,更精确的说法是“成员变量的构造”和“构造函数的执行体”。
成员变量的“构造”或“初始化”总是发生在构造函数体(即构造函数的{}
部分)执行之前。具体来说,成员变量的初始化有两种方式:
-
默认初始化:如果成员变量在初始化列表中没有被显式初始化,且没有提供默认构造函数,那么它们会使用默认构造函数(如果存在)进行初始化。对于内置类型(如int、char等),如果没有提供初始化,则它们的内容是未定义的(即它们不会被自动初始化为0或其他任何特定值)。
-
初始化列表:初始化列表(在构造函数定义时冒号
:
后面的部分)提供了一种在构造函数体之前初始化成员变量的机制。成员变量会按照它们在类定义中声明的顺序进行初始化,与它们在初始化列表中的顺序无关。
一旦所有成员变量都通过默认初始化或初始化列表进行了初始化,构造函数的执行体(即{}
中的代码)才会被执行。
以下是一个例子:
class MyClass {
public:
int a; // 成员变量a
MyClass(int valueA) {
// 构造函数体
std::cout << "Constructor body: a = " << a << std::endl;
a = valueA; // 在构造函数体内给a赋值
}
// 假设有一个成员变量b,通过初始化列表进行初始化
MyClass(int valueA, int valueB) : b(valueB) { // 初始化列表
// 构造函数体
std::cout << "Constructor body: a = " << a << ", b = " << b << std::endl;
a = valueA; // 在构造函数体内给a赋值
}
// 假设还有一个成员变量b,用于演示初始化顺序
int b; // 成员变量b,在a之后声明
};
int main() {
MyClass obj1(10); // 使用第一个构造函数
MyClass obj2(20, 30); // 使用第二个构造函数,并通过初始化列表初始化b
return 0;
}
在这个例子中,b
的初始化总是在a
之后,无论它们在初始化列表中的顺序如何。而且,你可以看到,在构造函数体中的代码执行之前,成员变量已经通过初始化列表或默认初始化进行了初始化。
5.c++ 11 新特性
C++11是C++编程语言的一个重要更新,它引入了许多新特性和改进。以下是一些C++11的主要新特性:
- 类型推导:C++11引入了auto关键字,用于自动类型推导。这允许程序员在声明变量时不必明确指定其类型,编译器会根据初始值自动推断变量的类型。
- 易用性的改进:C++11对语言的易用性进行了许多改进,例如通过引入新的循环结构(如基于范围的for循环)来简化代码。
- 类的改进:C++11对类进行了许多改进,包括默认成员初始化器、委托构造函数、final和override关键字等。这些改进使得类的编写更加灵活和清晰。
- 模板的改进:C++11对模板进行了改进,包括类型别名模板、函数模板的默认参数和可变参数模板等。这些改进使得模板的使用更加灵活和方便。
- 右值引用和移动语义:C++11引入了右值引用和移动语义的概念,这允许程序员更高效地处理临时对象和大型对象,从而减少了不必要的复制和内存分配。
- 智能指针:C++11引入了三种新的智能指针类型:std::unique_ptr、std::shared_ptr和std::weak_ptr。这些智能指针类型可以自动管理动态分配的内存,从而减少了内存泄漏和野指针的问题。
- 线程支持:C++11提供了对线程的原生支持,包括线程库、互斥锁、条件变量等。这使得C++程序员可以更方便地编写多线程程序。
- Lambda表达式:C++11引入了Lambda表达式,这是一种简洁地定义匿名函数对象的语法。Lambda表达式可以捕获其周围的变量,并在需要时执行特定的操作。
- nullptr:C++11引入了nullptr关键字,用于替代传统的NULL宏。nullptr是一个类型安全的空指针常量,它可以避免由于NULL宏导致的类型混淆和错误。
- 原始字符串字面量:C++11引入了原始字符串字面量(raw string literals),这使得程序员可以更方便地编写包含转义字符的字符串。
以上只是C++11新特性的简要概述,实际上C++11还有许多其他的新特性和改进。这些新特性不仅提高了C++语言的易用性和效率,还为程序员提供了更多的灵活性和选择。
链接
C++面试常见题
【面试】C\C++常见面试题30问_c++技术面试-优快云博客
https://www.cnblogs.com/inception6-lxc/p/8686156.html
嵌入式面试题 c语言
嵌入式C语言方面面试、笔试经典问题/题目_嵌入式面试笔试题-优快云博客
https://wenku.baidu.com/view/0cc24650df80d4d8d15abe23482fb4daa58d1db4.html