• Missing Semester 与 现代 C++ 笔记


    Missing Semester 与 现代 C++ 笔记

    基础知识

    • 需要对 C 语言和面向对象有基本的了解
    • 需要对 Linux Shell 有基本的了解
    • 自测: 请回答以下几组问题并阅读以下几组代码

    int 和 float 的计算机实现是什么?

    float Q_rsqrt( float x ) {
      union { float f; uint32_t i; } y;
      float x2 = x * 0.5F;
      y.f = x;
      y.i = 0x5f3759df - ( y.i >> 1 );
      y.f = y.f * ( 1.5F - ( x2 * y.f * y.f ) );
      // y.f = y.f * ( 1.5F - ( x2 * y.f * y.f ) );
      return y.f;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    类型转换

    int i = 1;
    char* pc = (char*)(&i);
    if(pc[0] == '\x1') {
        puts("This system is little-endian");
    } else {
        puts("This system is big-endian");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    大括号与作用域

    int main() {
       int i = 0;
       for ( ; i < ({
    	   int r = rand();
    	   printf("r == %d\n", r);
    	   (r + i)%10;
       }) ; i++ ); // gcc
       printf("i == %d\n", i);
       
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    指针与地址 (现代 C++ 中, 应该使用 using)

    T *p; p = &o; p = (T *) malloc(size);
    T0 *p(T1, T2); p = ( T0 (*)(T1, T2) ) pf;
    
    const T *p; T * const p;
    int n; int * const np = &n; int *const *npp = &np;
    // 指向指向 非const 的 int 的 const 指针的 非 const 指针
    
    int (*(*foo)(double))[3] = NULL;
    assert(foo == NULL);
    foo = ( int (*(*)(double))[3] ) fp;
    int bar = (*(*foo)(1.2))[1];
    
    void (*signal(int sig, void (*func)(int)))(int);
    // a better way
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int, sighandler_t);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    #include 
    
    int main()
    {
       int (*fp)(void) = &main;
       // int (*fp)(void) = main;
       // int (*fp)(void) = *main;
       int (**fpp)(void) = &fp;
       printf("fp == %p\n", fp);
       printf("&fp == %p\n", &fp);
       printf("&fpp == %p\n", &fpp);
       
       return 0;
    }
    
    // fp == 0x401122 // main .text
    // &fp == 0x7ffddaf5d8b8 // fp stack
    // &fpp == 0x7ffddaf5d8b0 // fpp stack
    // 函数调用也是运算符, 联系到函数调用的汇编实现, 一切就说的通了
    // 在编译器眼中, &func == func
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // file1.c
    char s[64] = "Hello World!\n";
    
    // file2.c
    extern char *s;
    int main() {
      printf("%p\n", s); // 防止 s 被优化掉
      // printf("%c\n", s[0]; // Segmentation fault
      return 0;
    }
    
    // extern 既非强符号,也非弱符号。
    // 无法匹配相同类型强符号之后就归入.bss节。
    // gcc file1.c file2.c
    // xxd a.out 会发现 "Hello World!\n" 位于 .rodata 节
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    动态分配内存

    volatile 变量是什么意思? 原子变量是什么意思?

    echo hello world 的执行过程?

    • 文件系统如何通过 inode 管理? 目录项 (路径) 和 inode 的关系?
      • shell 的文件操作
        • pwd, cd (本质上是 shell 的函数, 而不是一个可执行的二进制文件, 至少在 Linux 上如此)
        • ls
        • cp, mv, rm, ln, ln -s
        • mkdir, rmdir (empty only)
        • touch
        • find -type(d,f) -name -mtime -size -4k -size +4G -exec rm {} \;
        • chmod, chown, ls -l
        • 其他磁盘驱动有关的命令 dfdu (estimate) 的区别?
      • cat (head, tail, tee) 与 dd (DataDef) 的区别? (dd 可以调用 seek)
      • 重定向 >, < (>>, 1>, 2>, &>)
        • << 指定长文档的结束符
        • <<< 指定长字符串
        • 以上两种用法等同于stdin重定向为输入的长文档/长字符串
    • nc (NetCat) 与 curl (pup jq) 的区别? (curl 面向 HTTP)
    • echoprintf 的区别?
    • echo str, echo "str", echo 'str'
    • 桌面环境 (KDE plasma) 中程序的运行实际上也是 一个特殊的 shell 的子进程, 利用 pstree 命令可以观察到 plasmashell 的子进程有 code, netease, chrome.
    • 解释器提取 3 个 token, 依次是 echo, hello, world.
    • (不是布尔表达式; 没有管道;) 第一个 token, 也仅有这个 token, 指示一个可执行的二进制文件. 通过查找环境变量 (echo $PATH) 获得这个可执行目标文件的绝对路径 (实际上是 which echo 之一).
    • 之后的 2 个 token 是参数列表.
    • 执行 exec 库函数, 传入绝对路径和参数列表.
    • fork Shell 进程, 替换镜像, 高地址压入环境变量, 低地址 mmap 指令序列和数据 (.bss 初始化为 0). (实际上用户的虚拟地址有随机偏移.)
    • 从 _start 执行 echo, 通过系统调用, 把参数列表写到 stdout 上. _exit(status) 函数通过系统调用请求杀死进程, 给父进程发送信号 SIGCHLD, 父进程调用 wait 获得 status 作为返回值. (status 的语义是子进程的状态. 即, echo $? 结果是 0.)
    # include 
    
    __attribute__((constructor)) void hello() {
        printf("hello\n");
    }
    
    // See also: atexit(3)
    __attribute__((destructor)) void goodbye() {
        printf("goodbye\n");
    }
    
    int main() {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    环境变量

    • 函数 (或者仅仅是一条命令) 会在当前的 shell 环境中执行, 函数可以对环境变量进行更改.
    • 脚本会在单独的进程中执行 (脚本作为参数传给 SheBang 指定的解释器程序), 脚本需要使用 export 将环境变量导出, 并将值传递给环境变量.
    # leaf @ DAVE in ~/Roland/book/Cpp_Missing [16:14:15]
    $ sudo find -L /sys/class/backlight -maxdepth 2 -name 'brightness' 
    /sys/class/backlight/intel_backlight/brightness
    find: File system loop detected; ‘/sys/class/backlight/intel_backlight/subsystem’ is part of the same file system loop as ‘/sys/class/backlight’.
    
    # leaf @ DAVE in ~/Roland/book/Cpp_Missing [16:14:27] C:1
    $ sudo echo 50000 > /sys/class/backlight/intel_backlight/brightness
    zsh: permission denied: /sys/class/backlight/intel_backlight/brightness
    
    # leaf @ DAVE in ~/Roland/book/Cpp_Missing [16:14:35] C:1
    $ echo 50000 | sudo tee /sys/class/backlight/intel_backlight/brightness
    50000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • /sys/
    • File system loop 与 ln? (ls -i)
    • sudo 的位置, 时机?

    grep (GlobalREgexPrint) 与 awk (family names of its authors) 的区别?

    • grep 查找正则 pattern
      • grep -C Context
      • grep -v Invert
    • awk 是一个语言, Linux 上运行的是其实现 gawk.
    @include "filename"
    @load "filename"
    @namespace "name"
    pattern { action statements }
    function name(parameter list) { statements }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    管道 | 与重定向的区别?

    • 管道将前者的 stdout 作为后者的 stdin
    • 重定向将程序的 stdout (和 stderr) 替换为其他文件

    echo cat xargs 的区别?

    find . -type f | xargs -d "\n" ls -lt | head
    # 不含目录项(文件夹) 按时间列出 仅列出前几项
    
    • 1
    • 2
    • echo ... … -> stdout
    • cat filepath filepath 的内容 -> stdout
    • xargs command ... 执行 command stdin …

    su, sudo, useradd, usrdel

    jobsps 的区别?

    man

    public (protected) private

    this 指针, T & 成员函数返回 (*this)

    • istream &read(instream &is, T &obj);
    • ostream &read(onstream &os, T &obj);

    构造函数, 析构函数, 拷贝与赋值

    友元

    shell 语言

    foo=bar     # foo <- 'bar'
    foo = bar   # 向 foo 传入 ['=', 'bar']
    echo "$foo" # 特殊符号, 字段的替换
    # "$Foo" 为空串
    echo '$foo' # 字符串
    
    • 1
    • 2
    • 3
    • 4
    • 5
    #!/bin/bash
    # 通过 source 加载函数以及环境变量
    savepwd(){
        echo "$(pwd)" > $HOME/savepwd.cache
        echo "save pwd $(pwd)"
    }
    cdoldpwd(){
        # 注意小括号和引号的关系
        cd "$(cat "$HOME/savepwd.cache")"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    mcd () {
        mkdir -p "$1"
        cd "$1"
    }
    
    • 1
    • 2
    • 3
    • 4
    • $0 - 脚本名
    • $1$9 - 脚本的参数。 $1 是第一个参数,依此类推。
    • $@ - 所有参数
    • $# - 参数个数
    • $? - 前一个命令的返回值
    • $$ - 当前脚本的进程识别码
    • !! - 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用 sudo !! 再尝试一次。
    • $_ - 上一条命令的最后一个参数。
    echo "Starting program at $(date)"
    # date会被替换成日期和时间
    
    echo "Running $0 with $# arguments with pid $$"
    
    for file in "$@"; do
        grep foobar "$file" > /dev/null 2> /dev/null
        # 我们将标准输出流和标准错误流丢弃
        # 如果模式没有找到, 则grep退出状态 != 0
        if [[ $? -ne 0 ]]; then
        # [ ] 是 test 的语法糖 (同义词)
        # [[ ]] 是 bash 的关键字
            echo "File $file does not have any foobar, adding one"
            echo "# foobar" >> "$file"
        fi
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    convert image.{png,jpg}
    # convert image.png image.jpg
    
    cp /path/{foo,bar,jia}.sh /newpath
    # cp /path/foo.sh /path/bar.sh /path/jia.sh /newpath
    
    rm foo?
    rm foo*
    
    # 也可以结合通配使用
    mv *{.py,.sh} folder
    
    mkdir foo bar
    # 创建foo/a, foo/b, foo/c, bar/a, bar/b, bar/c
    touch {foo,bar}/{a..c}
    touch foo/x bar/y
    
    diff <(ls foo) <(ls bar)
    # 为什么不是 diff $(ls foo) $(ls bar)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    false || echo "not short circuiting"
    true && echo "not short circuiting"
    echo "this will" ; echo "always run" ;
    
    • 1
    • 2
    • 3
    • shellcheck
    • tldr (TooLongDon’tRead)
    • locate fd 作为 find 的替代品
    • rg ag ack 作为 grep 的替代品
    • fzf 改进 history
    • fasd, autojump 快速查找文件
      • broot nnn ranger 改进文件管理
    #!/usr/bin/env bash
    
    n=$(( RANDOM % 100 ))
    # 双小括号是 let 语句的语法糖, 而且是 POSIX 标准.
    # 意义是可以使用类似 C 的语法
    
    if [[ n -eq 0 ]]; then
        echo "Finished!"
        >&2 echo "End with n==$n"
        exit 1
    fi
    
    # And 符号在右侧提示文件描述符 >&1 >&2
    # 左侧的文件描述符直接书写 1> 2>
    # And 符号在左侧是语法糖, &> 展开成 1> 2>
    
    echo "Running."
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    count=0
    while true
    do
        ./random_try.sh &> /dev/null
        if [[ $? -ne 0 ]]; then
            echo "in total $count times"
            exit 0
        fi
        ((count++))
    done
    
    count=0
    for ((count=1;;count++))
    do
        ./random_try.sh &> /dev/null
        if [[ $? -ne 0 ]]; then
            echo "in total $count times"
            exit 0
        fi
    done
    
    count=0
    until [[ "$?" -ne 0 ]];
    do
        count=$((count+1))
        ./random_try.sh &> /dev/null
    done
    echo "in total $count times"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    正则表达式

    • . 除换行符之外的"任意单个字符"
    • * 匹配前面字符零次或多次
    • + 匹配前面字符一次或多次
    • [abc] 匹配 a, bc 中的任意一个
    • (RX1|RX2) 任何能够匹配RX1RX2的结果
    • ^ 行首
    • $ 行尾

    其他

    • ? 非贪婪 (本意是匹配恰好一个字符) sed 不支持 perl 支持
      perl -pe PATTERN
      
      • 1
    • [^ ]+ 匹配任意非空且不包含空格的序列

    快速上手shell

    cd, ls, tree, find.

    cd -
    find . -name FileName
    
    • 1
    • 2

    tldr

    ctrl+left ctrl+right home end 快速编辑命令 跳转

    ### workspace
    mkdir ~/mine/
    mkdir ~/mine/scripts/
    cd ~/mine/scripts/
    vim filename.sh
    
    ### activate
    chmod +x filename.sh
    mkdir ~/bin/
    ln -s $(readlink -f filename.sh) ~/bin/filename
    updcmd=$(printf "\n\nexport PATH=\$PATH:~/bin/\n")
    echo $updcmd >> ~/.bashrc
    source ~/.bashrc
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    TODO tmux oh_my_zsh vim.netrw(:Rexplore)

    ViM 编辑器

    buffer

    • :q 退出(关闭窗口)
    • :w 保存(写)
    • :wq 保存然后退出
    • :e {文件名} 打开要编辑的文件
    • :ls 显示打开的缓存
    • :help {标题} 打开帮助文档

    normal

    • 基本移动: hjkl (左, 下, 上, 右)
    • 词: w (下一个词), b (词初), e (词尾) +(下一行行首) -(上一行行首) ( ) (句子首位)
    • 行: 0 (行初), ^ (第一个非空格字符), $ (行尾)
    • 屏幕: H (屏幕首行), M (屏幕中间), L (屏幕底部)
    • 翻页: Ctrl-u (上翻), Ctrl-d (下翻)
    • 文件: gg (文件头), G (文件尾)
    • 行数: :{行数} 或者 {行数}G
    • 杂项: % (找到配对,比如括号或者 /* */ 之类的注释对)
    • 查找: f{字符}t{字符}F{字符}T{字符}
      • 查找/到 向前/向后 在本行的{字符}
      • , / ; 用于导航匹配
    • 搜索: /{正则表达式}, n / N 用于导航匹配
    • m 标记,' ` 跳转到标记

    visual

    • 可视化:v
    • 可视化行: V
    • 可视化块:Ctrl+v

    insert

    • i 进入插入模式
      • 但是对于操纵/编辑文本,不单想用退格键完成
    • O / o 在之上/之下插入行
    • d{移动命令} 删除 {移动命令}
      • 例如, dw 删除词, d$ 删除到行尾, d0 删除到行头。
    • c{移动命令} 改变 {移动命令}
      • 例如, cw 改变词
      • 比如 d{移动命令}i
    • x 删除字符(等同于 dl
    • s 替换字符(等同于 xi
    • 可视化模式 + 操作
      • 选中文字, d 删除 或者 c 改变
    • u 撤销, 重做
    • y 复制 (其他一些命令比如 d 也会复制)
    • p 粘贴
    • ~ 改变字符的大小写

    计数来结合“名词”和“动词”,这会执行指定操作若干次。

    • 3w 向前移动三个词
    • 5j 向下移动5行
    • 7dw 删除7个词

    修饰语改变“名词”的意义。修饰语有 i,表示“内部”,和 a, 表示“周围”。

    • ci( 改变当前括号内的内容
    • ci[ 改变当前方括号内的内容
    • da’ 删除一个单引号字符串, 包括周围的单引号

    . 重复上一个动作 q{字母} 录制 q 结束录制 @{字母} 重复 录制时可以递归调用该宏 q{字母}q 清除宏

    / ? 查找 n N 跳转到结果 * # 快速

    :s 替换 例如 %s/\[.*\](\(.*\))/\1/g 将有命名的 Markdown 链接替换成简单 URLs &:s 重复上一个替换

    :sp :vsp 分割 Ctrl+w 先导键

    :r 读入

    :e 新建 buffer
    bn bp 切换 buffer

    :tabnew 新建 tab
    gt gT 切换 tab

    Q 进入 Ex 模式

    H M L 页面

    J 合并行

    code vim

    跳转上一位置 alt+left alt+right / ctrl+i ctrl+o

    跳转段落 / ctrl+u ctrl+d { }

    括号跳转 ctrl+shift+pipe / percent

    快速插入 ctrl+enter ctrl+shift+enter / o O

    上下跳转 up down / gk gj

    光标位置不变 页面上下移动 ctrl+up ctrl+down / ctrl+e ctrl+y

    Pipe 用例 / 数据处理

    字符串运算:

    • gerp
    • awk
    • sed (弱)
    • tr (替换)
    • diff (比较不同)
    # 在远端首先过滤,再发送过滤好的流
    ssh SEVER_NAME 'journalctl | grep sshd | grep "Disconnected from"' > ssh.log
    less ssh.log
    
    # 用 sed 处理构成的表格的流
    cat ssh.log
     | grep sshd
     | grep "Disconnected from"
     | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
     | sort | unique -c # count
     | sort -nk1,1 # number, [1,1+1) 列
     | tail -n10
     | awk '{print $2}' | paste -sd, # 合并行, 并选择分隔符
    # sort -r 或者 head 实现倒序
    # uniq 只能过滤相邻的行, 所以必须先排序
    # 正则表达式中 \n awk 中 $n 的含义类似 shell
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    awk '$1 == 0 && $2 ~ /^[^ ]+$/ {print $0} | wc -l'
    
    • 1

    或者使用 << / <<<awk 输入以下语句:

    BEGIN { count = 1 }
    $1 == 0 && $2 ~ /^[^ ]+$/ { count += 1 }
    END { print count }
    
    • 1
    • 2
    • 3

    数学运算:

    • bc
    • R --slave -e python3 -c irb

    gnuplot 绘制图标

    承上启下

    missing semester 之后的内容包括

    • 元编程的思想
    • 日志, 调试与性能分析
      • 考虑增加 gdb 内容
    • (echo hello world 例子的深入) 命令行环境
    • git 的使用
    • docker, github, ssh

    与开发密切相关. 我们暂且忽略, 首先补全 C++ 面向对象的基础知识.

    • c n a m e \verb|c|name cname () 与 n a m e .h name\verb|.h| name.h 的区别: 是否位于 std 命名空间.
    • 机器无关类型: 引入了 std::string::size_type 类似 size_t .
    • 迭代器仅支持自增与自减, 形式上与指针相同. 为此, C++ 为 char a[] 引入了函数 begin end.
    # include 
    
    int main (int argc, char *argv[], char *envp[]) {
        for ( auto it = std::begin(argv[0]);
              it != std::end(argv[0]);
              it++ ) { }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • auto, decltype()
    • const int *pci, int *const cpi, constexpr int *cpi
    for (auto &c : str) { }
    
    • 1
    • throw, try, catch
    // 常见异常
    # include 
    # include 
    # include 
    # include 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 重载
    • 初始化
    # include
    using uint_list = std::vector<std::string::size_type>;
    int main() {
        // 构造函数传入参数
        uint_list x{4};   // [rand, rand, rand, rand]
                          // 与 allocator 有关
        uint_list y(4,0); // [0, 0, 0, 0]
        // 构造函数传入 initializer_list
        uint_list z{4,0}; // [4, 0]
        return 0;
    }
    
    /** T 是类型
      * para* 是一个实参
      */
    
    // 常量初始化, 引用初始化
    
    // 聚合初始化 (数组, POD)
    T array[3] = {para0, para1}; /* [T(para0), T(para1), T()] */
    T *parray[3] = {new T(para0), new T(para1) /*, nullptr*/};
    
    static T obj; T(); T obj{}; T obj = {}; // 零初始化
    T obj; new T; new T(); // 默认初始化
    T obj(); T obj{}; T(); T{}; new T{}; new T(); // 值初始化
    T obj(paras, ...); // 直接初始化
    /* 非class */ T obj{paras}; // 直接初始化
    static_cast<T>(another); // 直接初始化
    // 闭包内通过捕获的参数初始化
    // T::T : Tplus() { ... } T::T : Tplus{} { ... }
    
    T{paras, ...}; T obj{paras, ...}; new T{paras, ...}; // 直接列表初始化
    T obj = {paras, ...}; obj = {paras, ...}; // 复制列表初始化
    f({paras, ...}); /* return {paras, ...}; */ // 复制列表初始化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    int x0; // 如果在栈上 warning: 'x' is used uninitialized [-Wuninitialized]
    static int x_static; // 如果在 .bss 节(全局变量),被操作系统初始化为零。
    int x1{}; // int 执行零初始化
    int x2(); // 这是函数声明, 没有函数定义无法链接
    int x3 = {}; // 这也是“零初始化”的语法
    int *px0 = new int; // int 执行零初始化
    int *px1 = new int(); // 这也是“零初始化”的语法
    int *px2 = new int{}; // 这也是“零初始化”的语法
    
    class cComplex {public: double re; double im; Complex()=default;};
    struct sComplex {double re; double im;};
    
    cComplex cc0(); sComplex sc0(); // warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
                                    // g++ 认为是函数声明
    sComplex sc1{}; // 执行零初始化
    sComplex sc2 = {}; // 这也是“零初始化”的语法
    cComplex *pcc0 = new cComplex(); // 执行默认构造函数,初始化为0
    sComplex *psc0 = new sComplex(); // 执行零初始化
    sComplex *psc1 = new sComplex{}; // 这也是“零初始化”的语法
    
    /*** ** * ** ***/
    
    struct A { int x; int y; int z; };
    // A a{.y = 2, .x = 1}; // 错误:指派符的顺序不匹配声明顺序
    A b{.x = 1, .z = 2}; // OK:b.y 被初始化为 0 Warning: missing initializer for member 'main()::A::y' [-Wmissing-field-initializers]
    
    struct B {
      string x;
      unsigned int y = -1U;
      int z = -1;
    };
    B{.z=0}  // 以 {} 初始化 x,这样会调用默认构造函数
              // 然后以 = -1U 初始化 y
              // 然后以 = 0 初始化 z
    
    struct S {
        int x;
        struct T {
            int i;
            int j;
            int k[3];
        } y;
    };
    // 等价
    S s0 = { 1, { 2, 3, {4, 5, 6} } };
    S s1 = {1,2,3,4,5,6};
    S s2{ 1, { 2, 3, {4, 5, 6} } };
    S s3{1,2,3,4,5,6};
     
    // 类似指派符
    enum Foo { A, B, C=10, D, E=1, F, G=F+C};
    // A=0, B=1, C=10, D=11, E=1, F=2, G=12
    
    /*** ** * ** ***/
    
    // ===== C中合法,C++中不合法!=====
    
     数组
     error: ISO C++ does not allow C99 designated initializers [-Wpedantic]
    
    int array0[5] = {[4]=5,[0]=1,2,3,4}; // 保有 1,2,3,4,5
    int array1[MAX] = { // 开始初始化 a[0] = 1, a[1] = 3, ...
        1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0
    };
    // 对于 MAX=6 ,数组保有 1,8,6,4,2,0
    // 对于 MAX=13 ,数组保有 1,3,5,7,9,0,0,0,8,6,4,2,0(“稀疏数组”)
    
     乱序
     warning: missing initializer for member 'main()::A::x' [-Wmissing-field-initializers]
     warning: missing initializer for member 'main()::A::y' [-Wmissing-field-initializers]
    struct A { int x; int y; int z; };
    A a0{.z = 3, .y = 2, .x = 1};
    
     混合
     error: either all initializer clauses should be designated or none of them should be
    A a1{.x=1, 2, 3};
    
     嵌套
     error: expected primary-expression before '.' token
    struct B {A a;}
    B b0{.a.x=1};
    
    /*** ** * ** ***/
    
    // 调用 T::T()
    T obj; new T; new T(); // 默认初始化
    T obj(); T obj{}; T(); T{}; new T{}; new T(); // 值初始化
    
    // 调用相应参数的 T::T(para0, ...)
    T obj(para0, ...); new T(para0, ...);
    
    // 调用 T::T(std::initializer_list)
    T obj{item0, ...}; new T{item0, ...}; // 称作:直接列表初始化
    T obj = {item0, ...}; // 称作:复制列表初始化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • Allocator ⚠这是知乎上摘抄的一段代码,原链接找不到了.
    // allocator
    
    #include 
    
    template <class T> class MyAllocator{
    public:
        //下面是类型别名,实现中的可选项
        using value_type = T;
        using pointer = T *;
        using const_pointer = const T *;
    
        using void_pointer = void *;
        using const_void_pointer = const void *;
    
        using size_type = size_t;
        using difference = std::ptrdiff_t;
    
        //重新绑定函数
        template <class U>
        struct rebind{
            using other = MyAllocator<U>;
        };
    
        MyAllocator() = default;
        ~MyAllocator() = default;
    
        //分配内存
        pointer allocate(size_type numObjects){
            allocCount += numObjects;
            std::cout << "MyAllocator::allocate 分配内存对象个数: " << numObjects << std::endl;
            return static_cast<pointer>(operator new(sizeof(T) * numObjects));
        }
    
        //分配内存
        pointer allocate(size_type numObjects, const_void_pointer hint){
    
            return allocate(numObjects);
        }
    
        //释放内存
        void deallocate(pointer p, size_type numObjects){
            std::cout << "MyAllocator::deallocate 释放内存对象个数: " << numObjects << std::endl;
            allocCount = allocCount - numObjects;
            operator delete(p);
        }
    
        //分配器支持最大的内存数
        size_type max_size() const
        {
            return std::numeric_limits<size_type>::max();
        }
    
        // //构造对象
        // template 
        // void construct(U *p, Args &&... args)
        // {
    
        //     new (p) U(std::forward(args)...);
        // }
    
        // //销毁对象
        // template 
        // void destroy(U *p)
        // {
        //     p->~U();
        // }
    
        //返回每次分配/删除的内存数
        size_type get_allocation_count() const {
            return allocCount;
        }
    
    private:
        //统计当前内存的使用量
        size_type allocCount;
    };
    
    int main(){
        std::vector<int, MyAllocator<int>> v(0);
        for (size_t i = 0; i < 32; i++){
            v.push_back(i);
            std::cout << "[总计分配]" << v.get_allocator().get_allocation_count() << std::endl;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    MyAllocator::allocate 分配内存对象个数: 1
    [总计分配]1
    MyAllocator::allocate 分配内存对象个数: 2
    MyAllocator::deallocate 释放内存对象个数: 1
    [总计分配]2
    MyAllocator::allocate 分配内存对象个数: 4
    MyAllocator::deallocate 释放内存对象个数: 2
    [总计分配]4
    [总计分配]4
    MyAllocator::allocate 分配内存对象个数: 8
    MyAllocator::deallocate 释放内存对象个数: 4
    [总计分配]8
    [总计分配]8
    [总计分配]8
    [总计分配]8
    MyAllocator::allocate 分配内存对象个数: 16
    MyAllocator::deallocate 释放内存对象个数: 8
    [总计分配]16
    [总计分配]16
    [总计分配]16
    [总计分配]16
    [总计分配]16
    [总计分配]16
    [总计分配]16
    [总计分配]16
    MyAllocator::allocate 分配内存对象个数: 32
    MyAllocator::deallocate 释放内存对象个数: 16
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    [总计分配]32
    MyAllocator::deallocate 释放内存对象个数: 32
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    TiDB x 安能物流丨打造一栈式物流数据平台
    spring cloud 之 Netflix Eureka
    驱动开发:内核特征码扫描PE代码段
    为什么要使用MVP架构
    Mysql中完整性约束语法问题
    [计算机提升] 命令、批处理介绍
    虹科案例 | 欧洲核子研究中心使用虹科MSR数据记录仪进行运输监测
    前端React项目的Next.js项目通过CSS引入自定义字体文件
    制作一个简单HTML抗疫逆行者网页作业(HTML+CSS)
    ARM Linux 设备树详细介绍(1)
  • 原文地址:https://blog.csdn.net/int_main_Roland/article/details/126031676