C++面试知识点(二)

发布于:2025-02-10 ⋅ 阅读:(65) ⋅ 点赞:(0)

目录

1、define、const、typedef 的区别

2、constexpr

constexpr变量:

constexpr函数:

3、内存泄漏及避免

4、new和malloc,delete和free

5、面向对象三大特性

C++11新特性:

1、auto

auto与decltype 

2、范围基于的 for 循环

3、智能指针

4、 Lambda 表达式

5、移动语义

6、nullptr

7、std::thread 和多线程支持

8、 std::chrono

9、std::initializer_list

10、强类型枚举(enum class)

C++14 新特性

1、二进制字面量

2、std::make_unique

3、返回类型后置

4、泛型 lambda

C++17 新特性

1、std::optional

2、std::variant

3、std::any

4、结构化绑定

5、if constexpr

C++20 新特性

1、概念(Concepts)

2、范围库(Ranges)

3、协程(Coroutines)

4、模块(Modules)


1、define、const、typedef 的区别

define const typedef
发生在编译的预处理阶段,只是简单的字符串替换,使用多少次就多少次替换,没有类型检查不安全;可以用来防止头文件重复引用 用于定义常量生效于编译阶段,有类型,define也可以定义常量不带类型,生效于预处理阶段直接使用不放入内存 有对应的数据类型,在编译、运行阶段是要进行类型判断的;在静态存储区中分配空间,在程序运行过程中内存中只有⼀个拷贝

2、constexpr

constexpr 表示“常量”的语义,只能定义编译期常量,你将⼀个成员函数标记为constexpr,则顺带也将它标记为了const。如果你将⼀个变量标记为constexpr,则同样它是const的。但相反并不成立,⼀个const的变量或函数,并不是constexpr的。

constexpr变量:

复杂系统中很难分辨⼀个初始值是不是常量表达式,可以将变量声明为constexpr类型,由编译器来验证变量的值是否是⼀个常量表达式。初始化 constexpr int n = 1; constexpr int m = n + 1;

constexpr函数:

constexpr函数是指能用常量表达式的函数,函数的返回类型和所有形参类型都是字面值类型,函数体有且只有⼀条return语句。为了可以在编译过程展开,constexpr函数被隐式转换成了内联函数
constexpr和内联函数可以在程序中多次定义,⼀般定义在头文件。
constexpr 构造函数:构造函数不能是const,但字面值常量类的构造函数可以是constexpr。
constexpr构造函数必须有⼀个空的函数体,即所有成员变量的初始化都放到初始化列表中。对象调用的成员函数必须使用 constexpr 修饰。

3、内存泄漏及避免

释义:内存泄漏(memory leak)是程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误失去了对该段内存的控制,而造成了内存的浪费,常见于指针指向改变,未释放动态分配的内存。可以用Valgrind, mtrace进行内存泄漏检查。

常见内存泄漏:1)通过malloc,new等从堆中分配的⼀块内存,完成后须通过调用对应的 free或者 delete释放掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,产生Heap Leak。2)主要指程序使用系统分配的资源如 Bitmap,handle ,SOCKET 等没有用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。3)当基类指针指向子类对象时,如果基类的析构函数不是 virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露;

避免:将内存的分配封装在类中,构造函数分配内存,析构函数释放内存;使用智能指针。

4、new和malloc,delete和free

new malloc
C++的运算符,可以为对象分配内存并调用相应的构造函数 malloc 是C语言库函数,只分配指定大小的内存块,不会调用构造函数
new 返回的是具体类型的指针,而且不需要进行类型转换 malloc 返回的是 void* ,需要进行类型转换,因为它不知道所分配内存的⽤途
new 在内存分配失败时会抛出 std::bad_alloc 异常 malloc 在内存分配失败时返回 NULL
new 可以⽤于动态分配数组,并知道数组大小 malloc 只是分配指定大小的内存块,不了解所分配内存块的具体⽤途
delete free
delete 会调用对象的析构函数,然后释放内存,确保资源正确释放 free 只是简单地释放内存块,不会调用对象的析构函数
delete 释放的内存块的指针值会被设置为 nullptr ,以避免野指针 free 不会修改指针的值,可能导致野指针问题
delete 可以正确释放通过 new[] 分配的数组 free 不了解数组的大小,不适用于释放通过 malloc 分配的数组

5、面向对象三大特性

封装:将数据和代码捆绑在⼀起,避免外界干扰和不确定性访问;功能目地:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

C++通过public、 protected、 private 三个关键字(成员访问限定符)来控制成员变量和成员函数的访问权限;在类的内部(定义类的代码内部),无论成员被声明为 public、 protected、private都可以互相访问的,没有访问权限的限制。在类的外部(定义类的代码之外),只能通过对象访问成员,并且通过对象只能访问 public 属性的成员,不能访问 private、 protected 属性的成员。

继承:让某种类型对象获得另⼀个类型对象的属性和方法,它可以使用现有类的所有功能,并在不需重新编写原来的类的情况下对这些功能进行扩展;

无论公有继承、私有和保护继承,私有成员不能被"派生类"访问,基类中的公有和保护成员能被“派生类”访问。对于公有继承,只有基类中的公有成员能被“派生类对象”访问,保护和私有成员不能被“派生类对象”访问。对于私有和保护继承,基类中的所有成员不能被“派生类对象”访问。

多态:向不同对象发送同⼀消息,不同的对象在接收时会产生不同的行为(重载实现编译时多态,虚函数实现运行时多态)

多态性是允许你将父对象设置成为和⼀个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作;实现多态有两种方式: 1) 覆盖(override): 是指子类重新定义父类的虚函数的方式;2) 重载(overload): 是指允许存在多个同名函数,这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)
 

C++11新特性:

1、auto

auto:在 C++11 及之后的版本中,auto 关键字用于自动推导变量的类型,需要注意几点:

auto定义须马上初始化,否则无法推导出类型 auto不能用作函数参数
在类中auto不能用作非静态成员变量 auto不能定义数组,可以定义指针
auto无法推导出模板参数 在不声明为引用或指针时, auto会忽略等号右边的引用类型和cv(const和volatile)限定
auto在一行定义多个变量时,各个变量的推导不能产生⼆义性,否则编译失败 在声明为引用或者指针时, auto会保留等号右边的引用和cv属性

auto x = 42; // x 的类型为 int
auto y = 3.14; // y 的类型为 double

auto与decltype 

decltype:用于推导表达式类型,编译器分析表达式的类型但表达式实际不会进行运算decltype不会像auto⼀样忽略引用和cv属性, decltype会保留表达式的引用和cv属性,对于decltype(exp)有:
1) exp是表达式, decltype(exp)和exp类型相同
2)exp是函数调用, decltype(exp)和函数返回值类型相同
3)其它情况,若exp是左值, decltype(exp)是exp类型的左值引用

2、范围基于的 for 循环

提供了一种简洁的方式来遍历容器。

std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto& v : vec) {
    std::cout << v << " ";
}

3、智能指针

引入了 std::unique_ptr 和 std::shared_ptr,用于管理动态分配的内存,避免内存泄漏。

std::unique_ptr<int> ptr(new int(10));
std::shared_ptr<int> sharedPtr = std::make_shared<int>(20);

4、 Lambda 表达式

允许在代码中定义匿名函数,简化回调和函数对象的使用。

auto lambda = [](int a, int b) { return a + b; };
std::cout << lambda(2, 3); // 输出 5

5、移动语义

引入了移动构造函数和移动赋值运算符,优化了资源管理。

class MyClass {
public:
    MyClass(MyClass&& other) noexcept { /* 移动构造 */ }
    MyClass& operator=(MyClass&& other) noexcept { /* 移动赋值 */ }
};

6、nullptr

引入了 nullptr 关键字,替代 NULL,提供类型安全的空指针。

int* p = nullptr; // 明确表示空指针

7、std::thread 和多线程支持

引入了线程库,支持多线程编程。

#include <thread>
void func() { /* 线程执行的代码 */ }
std::thread t(func);
t.join(); // 等待线程结束

8、 std::chrono

提供了时间处理的标准库,支持高精度计时。

auto start = std::chrono::high_resolution_clock::now();
// 执行某些操作
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = end - start;

9、std::initializer_list

支持使用花括号初始化容器和其他类型。

std::vector<int> vec = {1, 2, 3, 4, 5};

10、强类型枚举(enum class)

提供了强类型的枚举,避免了命名冲突。

enum class Color { Red, Green, Blue };
Color c = Color::Red;

C++14 新特性

1、二进制字面量

支持使用二进制表示整数。

int x = 0b1010; // x 的值为 10

2、std::make_unique

提供了 std::make_unique 函数,简化 std::unique_ptr 的创建。

auto ptr = std::make_unique<int>(10);

3、返回类型后置

允许在函数定义中将返回类型放在后面,适用于复杂的返回类型。


auto func() -> int { return 42; }

4、泛型 lambda

允许在 lambda 表达式中使用模板参数。

auto lambda = [](auto a, auto b) { return a + b; };

C++17 新特性

1、std::optional

提供了一种表示可能缺失值的类型。

std::optional<int> opt;
if (opt) {
    std::cout << *opt;
}

2、std::variant

提供了一种类型安全的联合体。

std::variant<int, std::string> var;
var = 42; // 或者 var = "Hello";

3、std::any

提供了一种类型安全的容器,可以存储任意类型的值。

std::any a = 10;
a = std::string("Hello");

4、结构化绑定

允许将元组或结构体的成员直接绑定到变量。

std::tuple<int, double> t(1, 3.14);
auto [x, y] = t; // x = 1, y = 3.14

5、if constexpr

在编译时进行条件判断,适用于模板编程。

template<typename T>
void func(T value) {
    if constexpr (std::is_integral<T>::value) {
        // 整数类型的处理
    } else {
        // 非整数类型的处理
    }
}

C++20 新特性

1、概念(Concepts)

提供了一种约束模板参数的方式,增强了模板的可读性和可维护性。

template<typename T>
concept Incrementable = requires(T x) { ++x; };

2、范围库(Ranges)

提供了对容器和算法的更强大和灵活的支持。

#include <ranges>
auto view = vec | std::views::filter([](int n) { return n > 2; });

3、协程(Coroutines)

引入了协程的支持,简化异步编程。

#include <coroutine>
struct Task {
    struct promise_type { /* ... */ };
};

4、模块(Modules)

提供了一种新的代码组织方式,