面经(三)

auto类型推导

1. 基本类型推导:
   - `auto i = 42;` 推导为 `int`,因为42是整数字面量,默认类型为`int`。
   - `auto l = 42LL;` 推导为 `long long`,因为42LL是长整型字面量,`LL`后缀指定类型为`long long`。
   - `auto f = 3.14f;` 推导为 `float`,因为3.14f是浮点字面量,`f`后缀指定类型为`float`。
   - `auto d = 3.14;` 推导为 `double`,因为3.14是双精度浮点字面量,默认类型为`double`。

2. 指针和引用类型推导:
   - `auto* p = &x;` 推导为 `int*`,因为`&x`是取`x`的地址,`x`是`int`类型,所以地址是`int*`类型。
   - `auto& r = x;` 推导为 `int&`,因为`r`被声明为`x`的引用,所以`r`的类型与`x`相同,即`int`的引用。
   - `const auto& cr = x;` 推导为 `const int&`,因为`cr`是对`x`的常量引用,所以它是`int`的常量引用。

3. 数组和字符串字面量推导:
   - `auto arr = new int[10];` 推导为 `int*`,因为`new`操作符分配一个`int`类型的数组并返回一个指向数组首元素的指针。
   - `auto cstr = "hello";` 推导为 `const char*`,因为字符串字面量在C++中是常量字符数组,并且会退化为指向其首元素的指针。

4. STL容器推导:
   - `auto it = v.begin();` 推导为 `std::vector<int>::iterator`,因为`begin()`成员函数返回一个迭代器,指向容器的第一个元素。

5. 函数返回类型推导:
   - `auto func() -> int { return 10; }` 这里`auto`不涉及类型推导,它只是表明函数的返回类型将在尾置返回类型中给出,即`int`。

6. 范围for循环中的推导:
   - `for (auto& str : strings) { ... }` 在范围`for`循环中,`str`推导为`std::string&`,因为`strings`是`std::string`元素的集合,所以`str`是对集合中元素的引用。

7. 结构体和类成员推导:
   - `auto pt = Point{3.14, 4.15};` 推导为 `Point`,因为`{}`初始化器用于构造`Point`类型的对象。

8. 使用auto避免复杂类型名称:
   - `for (auto& pair : map) { ... }` 在这个循环中,`pair`推导为`std::pair<const std::string, std::vector<int>>&`,因为`map`的元素是这种类型的键值对。
   - `auto it = map.find("key");` 推导为 `std::map<std::string, std::vector<int>>::iterator`,因为`find()`函数返回一个迭代器。

9. Lambda表达式推导:
   - `auto lambda = [](int x) -> int { return x * x; };` 推导出的类型是一个编译器特定的未命名函数对象类型。尽管它类似于`std::function<int(int)>`,但不完全相同。它是一个有`operator()`成员函数的类,该函数接受一个`int`类型的参数并返回一个`int`。

10. decltype结合auto使用
    - `decltype(auto) var = 1;` 在这里`auto`不做类型推导,`decltype`指示编译器使用`1`的精确类型,即`int`。
    - `decltype(auto) var2 = y_ref;` 同样,`decltype(auto)`使得`var2`的类型与`y_ref`的类型相同,即`int&`。这是因为`decltype`捕获了`y_ref`的引用性质。

 左值右值 i++ ++i是左值还是右值 分别什么时候引入的

        在C++中,左值(lvalue)和右值(rvalue)是表达式的两种属性。左值是指那些地址可识别、具有持久状态的对象。右值是指可以被移动且不具有持久状态的临时对象,通常是直接用于赋值、计算和返回的值。

对于`i++`(后置递增)和`++i`(前置递增):

- `i++` 是右值表达式。它表示的是变量`i`在递增前的值,这个值是一个临时值,不能被赋予其他值。即使`i`本身是一个左值,`i++`表达式的结果却是一个右值。
- `++i` 是左值表达式。它递增`i`的值,并返回`i`的新值。因为`++i`实质上仍然是`i`,而`i`是一个左值,所以`++i`也是一个左值。

        左值和右值的概念在C语言中就已经存在,但C++在发展过程中对它们进行了进一步的细分,引入了新的表达式分类,包括将右值细分为纯右值(prvalue)和将亡值(xvalue),同时左值也被细分为通用左值(glvalue)。这些细分是为了支持C++11标准中新增的移动语义(move semantics)和完美转发(perfect forwarding)。因此,在C++11之前,我们通常只讨论左值和右值。C++11标准中引入了新的术语来描述这些概念,并且引入了`std::move`函数,该函数可以将左值显式转换为将亡值,从而利用移动语义。

进程线程协程

进程、线程、协程第一弹 —— 概念-CSDN博客

constexpr 和const

        `constexpr`和`const`是C++中两个不同的概念,尽管它们经常一起使用,但它们的目的和意义是不同的。

const

        `const`关键字用于定义一个常量值,这意味着一旦一个变量被定义为`const`,它的值就不能被改变。它可以用于变量、指针、类的成员函数等场合。示例:

const int a = 10;  // a是一个常量,不能被修改
int const b = 20;  // 同上,与a的情况相同,只是语法不同

const int* ptr1 = &a;  // ptr1是指向const int的指针,不能通过ptr1修改其指向的值
int* const ptr2 = ...; // ptr2是一个const指针,指向int,ptr2本身不能指向别的地址,但其指向的值可以修改

`const`可以应用于编译时和运行时的值。它更多地关注对象的不可变性。

constexpr

        `constexpr`是C++11引入的关键字,用于定义常量表达式。这意味着定义的变量或函数在编译时就必须有已知的值或行为,从而允许在编译期进行计算。这对于优化和使用字面量类型,如整数和浮点数,尤其有用,因为它允许在不牺牲性能的情况下使用更抽象的编程风格。示例:

constexpr int square(int x) {
    return x * x;
}

constexpr int squared_value = square(5);  // 编译时计算5 * 5

        在这个例子中,因为`square`函数是`constexpr`,所以它能在编译时计算出结果,而`squared_value`也被定义为`constexpr`,意味着它是一个编译时常量。

        `constexpr`指定的函数或对象必须满足特定的条件,以确保它们可以在编译时被评估。例如,`constexpr`函数体内不能包含任何不确定的元素,如非常量表达式或具有副作用的语句。

总结

- `const`用于声明常量,意味着一旦赋值后,值不能被改变。
- `constexpr`用于声明常量表达式,意味着值或函数必须在编译时是已知的,这使得`constexpr`对象或函数可以用于更多编译时的上下文中,比如数组大小、模板参数等。
- 所有`constexpr`变量都是`const`的,但不是所有`const`变量都是`constexpr`的。
- `const`更多地强调变量在运行时的不可变性,而`constexpr`强调编译时的值确定性和可能的编译时计算。

`__init__` 方法 和`__new__` 方法的区别

        在Python中,`__init__` 和 `__new__` 是特殊的方法,用于构建和初始化新对象。它们在创建类实例时发挥作用,但是它们的责任和调用时间不同。

`__new__` 方法

        `__new__` 是一个静态方法(虽然通常不需要显示地声明为 @staticmethod),它负责创建一个新的实例。`__new__` 方法是在一个对象实例化之前被调用的,它是真正的构造器。这个方法的主要任务是分配空间给新对象,并返回这个对象的引用。通常情况下,你不需要重写 `__new__` 方法,除非你在做一些高级的或特殊的事情,比如控制对象的创建过程。

`__init__` 方法

        `__init__` 方法通常被称为对象的“初始化器”,它在对象的内存已被分配后调用,用于初始化对象的状态。在 `__init__` 方法中,你会定义实例变量并设置它们的初始值。

        `__init__` 接收 `self` 参数,这是对新创建对象的引用,所以你可以在对象创建后设置对象的属性。


class MyClass:
    def __init__(self, value):
        self.value = value  # 初始化实例变量

obj = MyClass(5)
print(obj.value)  # 输出: 5


如果你需要自定义对象创建的过程,你可能需要重写 `__new__` 方法:
class MyClass:
    def __new__(cls, *args, **kwargs):
        # __new__ 必须要返回一个实例对象
        instance = super(MyClass, cls).__new__(cls)
        return instance
    
    def __init__(self, value):
        self.value = value

obj = MyClass(5)
print(obj.value)  # 输出: 5
在上面的代码中,`__new__` 方法被重写,但是它只是简单地调用了基类的 `__new__` 方法,并返回了实例对象。

总结:

- `__new__` 负责创建(分配内存)一个新的实例。
- `__init__` 负责初始化新创建的实例。
- 在大多数应用中,你只需要使用 `__init__` 方法。
- 如果需要自定义实例的创建过程(例如实现单例模式或其他高级用途),则可能需要重写 `__new__` 方法。

正则表达式 只匹配非字母数字且不包括空格的字符串

        如果你想要匹配一个字符串,确保它只包含非字母数字的字符,并且不包括空白字符(如空格、制表符和换行符),你可以使用以下正则表达式:

^[^\w\s]+$

这里的正则表达式分解如下:

- `^` 表示字符串的开始。
- `[...]` 表示字符集。
- `^\w\s` 表示匹配任何不是字母数字(`\w` 包括字母、数字和下划线)和不是空白字符(`\s` 包括空格、制表符和换行符)的字符。
- `+` 表示匹配一个或多个在它之前的字符集。
- `$` 表示字符串的结束。

这将匹配像 "?!#&@" 这样的字符串,但不会匹配包含字母、数字、空格或制表符的字符串。

shell脚本实现当前日期时间用户目录

#!/bin/bash

# 获取当前日期和时间
current_date=$(date "+%Y-%m-%d %H:%M:%S")

# 获取当前用户
current_user=$(whoami)

# 获取当前工作目录
current_dir=$(pwd)

echo "Current Date and Time: $current_date"
echo "Current User: current_user"
echo "Current Directory: current_dir"

git拉取一个commit

$ git cherry-pick commit的id

git 恢复缓冲区

git stash apply

git查看分支

git branch

字符串公共前缀

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

std::string longestCommonPrefix(const std::vector<std::string>& strs) {
    if (strs.empty()) return "";

    // 以第一个字符串作为起始比较基准
    std::string prefix = strs[0];
    
    // 遍历所有字符串
    for (const auto& str : strs) {
        // 逐个字符比较
        for (size_t i = 0; i < prefix.size(); i++) {
            // 如果当前位置的字符不同或者某个字符串已经比较完毕
            // 将prefix减少到当前长度
            if (i == str.size() || str[i] != prefix[i]) {
                prefix = prefix.substr(0, i);
                break;
            }
        }
    }
    
    return prefix;
}

int main() {
    std::vector<std::string> strs = {"flower", "flow", "flight"};
    std::cout << "The longest common prefix is: " << longestCommonPrefix(strs) << std::endl;
    return 0;
}

        这段代码首先检查输入的字符串数组是否为空。如果不为空,它使用第一个字符串作为比较的基准,并通过遍历每个字符串的每个字符来找出最长公共前缀。一旦在某个位置上发现字符不匹配,就将前缀减少到当前位置的长度,并继续比较余下的字符串。

        请注意,这个函数在存在极长的字符串或非常大的数组时可能会比较慢,因为它的时间复杂度是O(N∗M),其中N是字符串的数量,M是最短字符串的长度。针对特定情况有可能进一步优化算法的性能。

正则表达式 . * ?区别

1. 字面字符:
   - `a`, `1`, `B` 等都是字面字符,正则表达式会匹配这些字符本身。

2. 元字符:
   - `.` 匹配除换行符以外的任何单个字符。
   - `^` 匹配输入字符串的开始位置。
   - `$` 匹配输入字符串的结束位置。
   - `[]` 字符集合,匹配括号内的任一字符。
   - `|` 选择,匹配`|`前或后的表达式。
   - `()` 创建一个捕获组,用于从匹配的文本中提取值,或者指定优先级。

3. 预定义字符类:
   - `\d` 匹配任何数字,等同于 `[0-9]`。
   - `\D` 匹配任何非数字,等同于 `[^0-9]`。
   - `\w` 匹配任何字母数字字符,等同于 `[A-Za-z0-9_]`。
   - `\W` 匹配任何非字母数字字符,等同于 `[^A-Za-z0-9_]`。
   - `\s` 匹配任何空白字符,包括空格、制表符、换行符等。
   - `\S` 匹配任何非空白字符。

4. 数量限定符:
   - `*` 匹配前面的元素零次或多次。
   - `+` 匹配前面的元素一次或多次。
   - `?` 匹配前面的元素零次或一次。
   - `{n}` 匹配前面的元素恰好 n 次。
   - `{n,}` 匹配前面的元素至少 n 次。
   - `{n,m}` 匹配前面的元素至少 n 次,但不超过 m 次。

5. 转义字符:
   - `\` 用于转义特殊字符,例如:`\.`, `\^`, `\\`。

6. 特殊构造(非捕获):
   - `(?:...)` 非捕获组,匹配括号中的表达式,但不提供匹配的子串。
   - `(?=...)` 正向前瞻断言,匹配的子串后面必须紧跟着括号内的表达式。
   - `(?!...)` 负向前瞻断言,匹配的子串后面不能紧跟着括号内的表达式。

7. 标志(通常是正则表达式的一部分,但在某些环境中可能以函数参数或其他方式指定):
   - `i` 忽略大小写。
   - `m` 多行模式,`^` 和 `$` 也会匹配行的开始和结束。
   - `s` 允许`.`匹配包括换行符在内的所有字符。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值