碎碎念

本篇多为课堂笔记和做题时整理的知识点,可能存在错误和不全面的地方
感谢阅读~

正文

刚开始记的零零散散的玩意

保留N位小数

1
2
3
4
5
#include <iomanip> // 包含IO操控器
using namespace std;

// 设为定点格式、N位精度。之后持续生效。
cout << fixed << setprecision(N);

万能头文件:#include <bits/stdc++.h

原码&补码

一个二进制位表示符号(0正1负)

补码:正整数补码=二进制原码

负整数补码 = 相应正整数的各个二进制位取反后加1

空白符

空格符、水平制表符(\t)、垂直制表符(\v)、回车符(\r

换行符(\t)、换页符(\f

续行符

反斜杠(\)后面紧跟一个换行符(回车)构成

单/双目操作符

单目操作符:运算所需变量为一个的运算符

双目运算符:运算所需变量为两个的运算符

sizeof & typedef

可通过 sizeof(类型名/变量名) 来计算各种数据类型的数据所占的内存空间大小

标准库头文件 climit /limits.h 定义了所有整型的取值范围

cfloat / float.h 定义了所有实数类型的取值范围

typedef 已定义的类型 新的类型定义新的类型来代替已有的类型

内联成员函数

为了提高运行时的效率,对于较简单的函数可以声明为内联形式

内联函数体中不要有复杂结构(如循环语句和switch语句)

位操作符

  • 按位取反(~)(单目操作符)

    将操作数的每一个二进制位取反所得到的值

    十进制 4 0100

    ~4 1011

  • 按位与(&)

    两个操作数的二进制位分别按位进行与运算

    0&0 -> 0        0&1 -> 0        1&0 ->0        1&1 ->1

    0x3F80 & 0X7AE5

    0011111110000000 &

    0111101011100101 ->

    0011101010000000

  • 按位或(|)

    两个操作数的二进制位分别按位进行或运算

  • 按位异或(^)

    两个操作数的二进制位分别按位进行异或运算

    (x^a)^a = x

image-20230919124718465

  • 移位操作

    左移(<<):把第一个操作数按二进制依次左移由第二个操作数指定的位数。高位舍弃、低位补0

    右移(>>):右移。低位舍弃,对无符号数或有符号的非负数,高位补0;对于有符号数的负数,高位与原来的最高位相同

1
2
3
4
5
6
7
8
9
10
11
12
13
a & 1 == 0 //判断奇偶

void swap(int &a,int &b){
a ^= b; b ^= a; a ^= b;
}//交换两数

return ~a+1; //变换符号

// 求绝对值
int my_abs(int a){
int i = a >> 31;
return i == 0?a:(~a+1);
}

lambda表达式(匿名函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
[OtherVar](int x,int y) -> int {
return OuterVar + x + y;
}
*/
auto f = [](int a,int b) -> int{
return a+b;
};
cout << f(1,2) << endl; // 3

int N =100,M = 10;
auto g = [N, &M](int i) {
M = 20;
return N * i;
};

cout << g(10) << endl; //1000
cout << M << endl; //20

正则表达式

推荐的在线测试工具 regex101.com

  • 限定符? 表示其前面的字符可有可无(0次或1次)

    used* —— use used

  • 限定符*表示其前面的字符可以没有也可以出现多次

    ab*c —— ac abc abbbbbc

  • 限定符+ 匹配出现1次以上的字符

    ab+c         —— abc abbbbbc

  • {} 限定字符出现次数

    ab{2}c    —— abbc

    ab{2,6}c —— abbbc abbbbbbc

  • [abc]+ 匹配由abc这几个字母构成的单词

    可以在方括号内指定字符范围

    [a-z]+代表所有小写英文字符

    [a-zA-Z]+代表所有英文字符

    [a-zA-Z0-9]+ 所有英文字符和数字

    ^ 表示所有除后面列出的字符以外的字符

    [^0-9]+代表所有非数字字符 (包括换行符)

  • 元字符

    \d    数字字符

    \D    非数字字符

    \w    单词字符(英文、数字及下划线)

    \W   非单词字符

    \s     空白符(包含制表符及换行符)

    \S     非空白符

    \b    代表单词的开头或结尾,也就是单词的分界处

    . 在正则表达式中代表除换行符以外的任意字符

    ^ 只匹配行首    $ 只匹配行尾字符

    eg. ^a    —— absorb tea(×)

    a$    —— abc(×) tea

  • 贪婪与懒惰匹配

    *、+、{}在匹配字符串的时候会匹配尽可能多的字符

    只要在其右边加一个?,就可以把贪婪匹配转换成懒惰匹配

    1
    2
    3
    4
    <span><b> **This is a sample text**</b></span>
    <!-- <.+>会匹配所有字符 -->

    <!-- <.+?> 只会匹配html标签<>中的内容 -->

image-20230919124815073

image-20230919124832916

基本数据类型&表达式

  • 基本数据类型

    int a =4; 整数类型

    unsigned short b = 1; 无符号短整数(只有正数)

image-20230919124850084

  • 符号常量

    使用编译预处理指令定义

    #define PROCE 30 不是语句,末尾不要分号

  • 左值表达式 & 右值表达式

    能出现在赋值操作符左边的表达式为左值表达式,否则是右值表达式

    (左值表达式也能出现在赋值操作符右边)

    左值表达式的结果有明确的内存地址(能显式地访问该地址所指出的内存单元中的内容)

    右值表达式的结果无明确的内存地址,计算结果可以放在临时存储单元中。

  • 表达式

    由操作符、操作数以及圆括号所组成的运算式

    • 算数表达式 (a+b)*c/12-max(a,b)

    • 关系/逻辑表达式 (age < 10) && (weight > 30)

    • 地址表达式 &a[0]+2

      image-20230919124914889

    • 逗号表达式

      整个表达式顺序求解,结果为最后一个表达式的值

      a=(3+4,5*6,2+1) // a=3

      a=3*3,a+6,a+7; //a=9

函数的参数

  • 形参:定义被调函数时用到的变量声明,必须要定义类型

  • 实参:实际函数,被调函数使用时的参数值,必须是确定的数值

程序语言

c++是系统程序语言、混合式语言、编译型语言

  • 低级语言

    • 机器语言

    • 汇编语言

  • 高级语言(通常所讲的程序设计语言)

    • 过程式语言(C)

    • 面向对象语言(Java)

    • 函数式语言(Lisp)

    • 逻辑式语言(Prolog)

    • 混合式语言(C++)

常量

  • 整数常量

    • 前缀 : 030(8进制),0x30(十六进制)

    • 后缀:30l,30L,30ul(无符号长整型30)

  • 实数常量

    • 十进制形式:23.0 , 24.5 , 3.1234l

    • 指数形式: 23E1 , 145e-1 , 323441e1

结构类型

用于表示由固定多个、类型可以不同的元素所构成的复合数据

struc <结构类型名> {<成员表>};

  • 结构类型的成员可以是任意除void与本结构以外的C++类型,结构成员之间在逻辑上没有先后次序关系,但定义次序会影响它们的存储安排

  • 定义一个结构类型时不能对其成员进行初始化

    类型只是一个描述,它们不占有内存空间,对其初始化没有意义

  • 结构类型的变量可以在定义时同时定义

    1
    2
    3
    4
    5
    struct A
    { int x;
    int y;
    } a,b;
    // a,b是结构类型a的变量

数组

使用typedef定义数组类型

1
2
typedef int A[10];//A是数组类型
A a; //相当于 int a[10];

一维字符数组(字符串类型的实现)

通常在字符串中最后一个字符的后面存储一个字符\0 作为字符串的结束标记

因此char s[10]中字符数组变量s可以表示的字符串的最大长度为10

  • 字符数组的初始化

    1
    2
    3
    4
    5
    6
    char s[10] = {'h','e','l','l','o','\0'};
    //对于第一种形式,程序中必须显式地加上'\0'
    char s[10] = {"hello"};
    char s[10] = "hello";
    char s[] = "hello";
    //其他形式初始化会在最后一个字符的后面自动加上'\0'

二维数组

c++二维数组是传统意义的table,m行n列,本质是一块连续的内存空间,不是真正的一维数组的数组

image-20230919124945982

  • 二维数组的初始化

    1
    2
    3
    4
    int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    int a[3][4] = {{1},{5},{9}};
    int a[3][4] = {{0,1},{5}};

    定义时可以省略高维,但低维不可省 int a[][4] 正确

    n维数组定义的时候只能省略最高维

string类

  • 字符串变量可以被整串赋值,也可以修改其单个字符

    image-20230919125003062

指针

指针是内存地址的抽象表示,一个指针代表一个内存地址

从形势上看,指针属于无符号整数,但从概念上讲,指针与无符号整数是有区别的

指向数据的指针类型通常与该类型的指针变量同时定义

1
2
3
4
5
6
7
8
9
10
// <类型> *<指针变量>
int *p; //或 int* p;

//指针变量的赋值
int i,*p; p=&i; *p = 5; // i=5;
int i; int *p=&i; *p = 5; // i=5;

//定义语句中,*表示定义指针,非定义语句中,*表示间接访问运算
//定义语句中,&表示定义引用,非定义语句中,&表示“取变量的地址”运算
//一个指针变量只能指向同一类型的变量

void指针类型(也称空类型指针、万能指针)

指针变量里储存的地址,可以是常量的地址,也可以是全局变量、局部变量、堆(new)变量的地址,或无类型地址,也可以是函数的地址

  • void指针可以指向任何贷类型的地址

    void *p = &x; //x可以是基本数据类型,或对象/结构体类型

    不知道这块空间的确切用途时,使用空类型指针

  • 空指针在使用(获取内容)时必须转换

    1
    2
    3
    4
    int a = 3;
    void *p1; p1 = &a;
    int *pi = (int*)p1; //void*转换为int*,语法逻辑均正确
    float *pf = (float*)p1; //语法正确,逻辑错误

用途

  • 函数参数的双向传递

    指针变量作为函数参数,将变量的地址传送到被调函数中,被调函数内外共享一个变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //正确的实现——通过p1、p2把内存单元的数值进行交换
    void swap(int *p1,int *p2)
    { int t;
    t=*p1; *p1=*p2; *p2=t;
    }
    //错误的实现(交换p1、p2的地址)
    void swap2(int *p1,int *p2)
    { int *tp;
    tp=p1; p1=p2; p2=tp;
    }

    读入浮点数、将整数部分与小数部分分别输出

    image-20230919125021421

  • 自由访问数组

    数组在内存中占据一段连续的但愿,通过首地址+偏移可以访问到每个元素

    实际应用中,通常额外引入一个新的指针变量p,通过++、–在数组上游走

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    int a[10],*p;
    p = a; //或 p = &a[0];
    p++;
    //可以通过p++指向下一个整数,但不可以a++
    //虽然数组名a也是指针,但是常量,不可改变
    //p+1指向数组的下一个元素,而是下一个字节

    int a[10]; int *p=a;
    *p = 1; //等价于 a[0]=1,或 *a = 1;
    *(p+1)=2; // 等价于a[1]=2;或者*(a+1)=2;或p++;*p=2;

    int a[5] = {5,4,3,2,1};
    int *p = a; int b;
    b = *p++;
    //后置的++、--比*优先级高,*p++ 等价于 *(p++),++作用于指针p,而不是*p
    //*p++作用,迭代器,取当前元素,指向下一个
    //相当于 x = scan.nextInt();
  • 函数与指针

    指针变量指向一个函数——存放函数的地址

    函数名代表函数的入口地址,存放函数的地址。专门存放函数地址的指针变量称为指向函数的指针变量,一般形式返回类型(*指针变量名)(参数列表)

    通过指针访问函数,实现动态绑定,是多态的实现机制

    返回指针值的函数:被调函数的返回结果是一个地址,函数类型是指针类型(返回地址所指向的空间应当是主调函数中开辟的,而非被调函数中分配的空间,因为被调函数的局部变量已经被释放了

    • 指针数组和指向指针的指针

    • 指针数组是一个元素均为指针类型的数组,每一个元素都可以放地址 eg. int *p[4]; []比*优先级高

    • 二维数组的强化版,指针数组支持变长,且地址不连续,数据规模较大较复杂时,选用指针数组;矩阵运算、表格处理等规模确定、数据规整的,一般选择二维数组

    • 指向指针的指针:p为指针变量,p也有地址,可以再用一个指针变量指向p

    1
    2
    3
    4
    5
    6
    int i,*p;
    p = &i;
    int **pp;
    pp = &p;
    pp = &&i //错误,不能连续取地址
    pp = &i; //错误,pp为指向指针的指针变量,其基类型是指向整型数据的指针变量,而非整型数据

        经典用途:字符串数组

    image-20230919125038070

    • 指针数组作main()函数的形参:main函数带参数,参数由命令行传入

      main函数形参的形式

    1
    2
    3
    4
    5
    6
    7
    8
    main(int argc,char *argv[]) //main(int argc,char **argv)
    /*
    argc为命令行中参数的个数(包括文件名)
    argv为指向命令行中参数(字符串)的指针数组
    eg.
    S9_16.EXE CHINA JAPAN AMERICAN<CR>
    argc = 4 argv[1] = CHINA
    */
      1. 指针变量可以有空值,即不指向任何地址 int *p;p = NULL;

      2. 俩指针可以相减,不可相加。若进行相减运算,指向同一数组,相减结果为相距的数组元素个数

      3. 指向同一数组的两个指针可以比较大小(同相减)

  • 动态分配内存

根据实际需要开辟空间,静态开辟的内存会存在不足或浪费

  • new运算符开辟空间,空间的地址必须用指针保存下来,才不会丢失

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int *p;
    p = new int;
    p = new int(6); //赋初值6
    p = new int[6]; //申请一个数组,首地址送给p

    //两次空间申请
    int **pp;
    pp = new int*[100]; //int*[rows]
    for(int i=0;i<100;i++)
    pp[i] = new int[5];

    当内存中没有足够的空间给予分配时,new运算符返回空指针NULL(0)

  • 用new开辟的内存但愿如果不主动收回,这段空间会一直存在,直到重新开机

    delete运算符用来将动态分配的内存空间归还给系统

    delete收回new开辟的单个空间

    只有用new开辟的空间才能用delete收回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //单个空间
    int *point;
    point = new int;
    delete point;

    //连续空间
    int *point;
    point = new int[10];
    delete []point;
  • const指针

    const修饰指针的三种可能

    1. p只能指向一个地址,不能改变,指向的内容可变

      int r=6; int *const pr=&r;

    2. 指向常量的指针变量(禁写间接引用)

      1
      2
      3
      4
      5
      6
      7
      // const 数据类型* 指针变量名;
      const int *p; int a=100,b=30; p=&a;
      *p = 10; //错误,不能通过指针重新赋值
      a = 10; //正确
      *p = &b; //正确,可以对指针p进行改写
      //保证指针p指向的内容为只读(不可改写)
      //保证函数内部只读访问指针指向的内容

      att: 不能把指向常量的指针赋值给一般指针,反过来可以(不能通过赋值得到读写权限)

      1
      2
      3
      4
      5
      6
      7
      int a;
      const int *p1 = &a;
      int *p2 = &a;

      p1 = p1; //ok
      p2 = p1; //error
      p2 = (int *)p1; //ok 强制类型转换
    3. 指向常量的常指针变量

      const 数据类型 *const指针变量名;

      定义后,不允许通过指针变量改变所指向对象的值,也不允许改变指针变量的值。

  • 引用:对变量起别名

    <类型> &<引用变量名> = <原变量名>;

    1
    2
    3
    int a;
    int &ref_a = a;
    //ref_a并没有重新开辟内存单元,与a在内存中占用同一地址

    image-20230919125108282A项居然……

动态变量的撤销

c++程序运行期间,动态变量不会自动消亡,即使是在函数调用中创建的,函数返回后它依然存在,直到程序运行结束

一般情况下,用new创建的动态变量,用delete使之消亡;用malloc产生的,则使用free使之消亡

1
2
3
4
5
6
7
8
9
10
11
//delete <指针变量>
int *p = new int;
delete p; //撤销p所指向的动态变量

//delete []<指针变量>
//void free(void *p) 释放p所指向的内存空间
int *p = (int *)malloc(sizeof(int));
int *q = (int *)malloc(sizeof(int)*20);

free(p);
free(q);

用二者撤销动态数组时,其中指针变量必须指向数组的第一个元素

运算符重载

函数重载是指完成不同功能的函数可以具有相同的函数名、不同的参数个数/类型,仅返回值不同时,不能定义为重载函数

运算符重载:为运算符定义一个重载函数,遇到运算符时根据不同的运算对象调用对应的函数

1
2
3
4
5
6
7
8
// <返回类型> operator <运算符>(<参数表>)
// operator 是运算符重载函数关键字,operator<运算符>是函数名
Class Vector{
Vector operator + (Vector &);
}
Vector v2,v3;
Vector v1 = v2 + v3;
v1 = v2.operator+(v3);

运算符重载函数通常是类的成员函数友元函数,函数参数通常是类的对象

image-20230919125136729

+= 重载函数返回值类型为void

+= -= *=等修正运算,不会产生新的对象

image-20230919125148737

运算符重载为成员函数的参数只能有两种情况:没有参数或带一个参数

对单目运算符++,通常不能有参数

对双目运算符,只能带一个参数,可以是对象、对象的引用或其他类型

image-20230919125301795

重载不改变运算符的优先级和结合律,不改变操作数的个数

静态成员函数不能是运算符重载函数

a.operator+(b), operator+必然是属于对象a的函数,而不是类函数(静态函数)

成员函数 vs 友元函数

  • 运算符重载为类成员函数,成员函数所属对象是一个操作数,另一个操作数是函数参数

    1
    2
    3
    4
    A a,b,c;
    c = a+b; //c=a.operator+(b); 有参有返回值
    c = ++a; //c=a.operator++(); 无参有返回值
    c += a; //c.operator+=(a); 有参无返回值
  • 运算符重载为类的友元函数或普通函数,参与运算的对象全部成为函数参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //形参前自行添加const
    friend A operatpr+(A &a,A &b)
    c = a+b; //c=operator+(a,b);

    friend A operator++(A &a)
    c = ++a; //c=operator++(a);

    friend void operatpr+=(A &c,A &a)
    c += a; //operator+=(c,a);

    如果参数中有基本数据类型,运算符建议重载为成员函数(限制使用者的格式)

    image-20230919130433828

  • C++规定

    • 赋值运算符=、下标运算符[]、函数调用运算符()、成员运算符 ->只能定义为类的成员函数

    • 一般将单目运算符(++ !)、复合运算符(+=)重载为成员函数 如a++;a+=b;

    • 流输出、流输入运算符(<</ >>)、类型转换运算符只能作为友元函数

    • 习惯将双目运算符重载为友元函数

  • 自加运算符重载

    虽然运算后对象a的值一致,但先自加和后自加的函数返回值不一致,必须在重载时予以区分。参数的存在只是为了区分前置和后置

    1
    2
    3
    4
    5
    6
    7
    8
    A a,b;
    b = ++a;
    A operator++(){...}
    A operator++(A a){...}

    b = a++;
    A operator++(int){...}
    A operator++(A a,int){...}
  • 流运算符的重载

    image-20230919125334176

  • 赋值运算符重载

    当对象的成员中使用了动态的数据类型时(用new开辟空间),必须要重载赋值运算符=,否则在程序的执行期间会出现运行错误

    (主要是在析构时,若a = b;a与b指向同一块空间,析构时会对该空间进行两次delete操作,从而报错)

    image-20230919125408947

  • 类型转换函数重载

    类型转换必须重载为类的成员函数,不能是友元函数

    类型转换函数没有参数,被转换的是用户自定义对象

    image-20230919125424660

    若想把A类型转换为double类型A::operator double(){}

泛型

在程序中,以类型作为参数

泛型函数——包含类型参数的函数;泛型类——包含类型参数的类

两种机制:通用指针类型的参数、函数模板

  • 通用指针类型的参数

    1
    2
    3
    void qsrt(void *base,int nelem,unsigned int width),
    int(*pfCompare)(const void *,const void *))
    //数组起始地址 base 数组元素的个数 nelem 每个元素的大小 width
  • 函数模板

    1
    2
    3
    4
    5
    6
    7
    8
    template<class T>
    void sort(T elements[],unsigned int count){}
    int a[100];
    sort(a,100); //对int类型数组进行排序

    A c[300]; //类A需要重载操作符<,需要时还应
    //自定义拷贝构造函数和重载操作符=
    sort(c,300);

    image-20230919125444207

    显式地实例化 max<double>(1,2.0)

    函数模板中也可以带非类型参数(配合数组食用)

    image-20230919125509933

    不能用变量实例化非类型参数 即不可以cin >> x; f<int,x>();

  • 类模板

    如果一个类的成员的类型可参,称该类为模板类,用类模板实现

    image-20230919125524005

    模板类的实例化需要在程序中显式地指出

    1
    2
    3
    4
    5
    6
    Stack<int> st1;  	//创建一个元素为int型的栈对象
    int x; st1.push(10);st1.pop(x);
    Stack<double> st2; //创建一个元素为double型的栈对象
    double y; st2.push(1.2);st2.pop(y);
    Stack<A> st3; //创建一个元素为A类型的栈对象(A为定义的一个类)
    A a,b; st3.push(a); st3.pop(b);

    静态成员仅属于实例化后的类,类模板的实例类之间不共享静态成员

    只有在模板类的实现代码中,才对其实例化

    image-20230919125935568

拷贝构造函数

创建对象时,用另一个同类的对象对其进行初始化

A(const A &a);const可省略,为防止在函数中修改实参对象

一般来讲会调用拷贝构造的情况

  • 定义对象时

    1
    2
    3
    A a1; //
    A a2(a1); //调用拷贝构造用a1对a2进行初始化
    //也可写成 A a2=a1; 或 A a2=A(a1);
  • 把对象作为值传给参数

    1
    2
    3
    void f(A x); //参数类型不是指针或引用
    A a;
    f(a); //调用时创建形参对象x,用对象a对x初始化
  • 把对象作为值返回

    1
    2
    3
    4
    5
    6
    7
    A f()
    { A a;
    ...
    return a; //创建一个类A的临时对象,并调用拷贝构造函数用a对其进行初始化
    }
    A a1;
    a1 = f();//把函数f返回的临时对象赋值给对象a1

tips:

  1. 若使用了控制符,加头文件 <iomanip>

    注意 setprecision() 单独用时只代表 “有效位数”;

    setiosflags(ios::fixed) 连用时代表 “小数位数”

  2. cin只能输入一个单词,遇到空格输入截止

    使用cin.getline(数组名,最大字符数)读入一行

    1
    2
    char s1[80];
    cin.getline(s1,80);
  3. c语言中提供了一些字符串处理函数,需要#include <string.h>#include <cstring>头文件

    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
    char str1[20]={"I am a"}; char str2[] = {"boy"};
    //合并 strcat ,要求第一个串有足够空间可以存放两个合并串;
    strcat(str1,str2); 、// I am a boy
    //复制 strcpy ,覆盖掉str1对应位置的字符,其他不变
    strcpy(str1,str2); //boy
    //字符串赋值的正确操作
    char str1[20]; strcpy(str1,"CHINA");

    str1 = "CHINA"; //c++中语法错误

    //字符串大小的比较
    int stcmp(str1,str2);
    if(strcmp(str1,str2)>0) // str1>str2
    // 对于c++的string对象,使用><==进行比较

    //求字符串长度的函数
    strlen(str1)
    /*
    参数为数组名,返回值为首字母到\0的长度
    而非数组在内存空间中的大小
    */

    char s[80];strcpy(s,"abcd");
    cout << strlen(s) << endl; //输出4
    cout << sizeof(s) << endl; //输出80
  4. 讲得很详细,关于拷贝构造函数、对象数组(指针)、static、const和友元

    # 拷贝构造函数、对象数组(指针)、static、const、友元)

  5. malloc函数在程序的堆区中分配一块大小为size的内存空间,并返回该内存空间的首地址(类型为void );若需将该空间用于储存某个具体类型的数据,需要*对返回值类型进行强制类型转换

    1
    2
    3
    4
    5
    6
    7
    8
    int *p1,*p2,*r;
    typedef int A[20];
    A *q;
    int m,n;

    p1 = (int *)malloc(sizeof(int)); //创建一个int型动态变量
    p2 = (int *)malloc(sizeof(int)*n); //创建一个由n个int型元素所构成的一维动态数组变量
    q = (A *)malloc(sizeof(int)*20*n); //创建一个n行20列的二维动态数组变量
  6. 智能指针(看不大懂,还用上了lambda)

  7. string str = s.substr(1,2); // 第一位为起始下标,第二位为取子串的长度

    int i = stoi(str); // str转int

  8. 虚析构函数

  9. 王政老师的cpp笔记1

选择知识点

  1. 在 C++中,源程序编译可执行程序的正确顺序是:编辑(.cpp/.h)、编译(.obj)、链接(.exe)、执行(运行)

  2. struct默认的访问权限是public,而class默认的访问权限是private

  3. 静态数据成员是类所有对象共享的数据(同一个类多个对象共享一个静态数据成员

  4. 【引用传递】形参为引用类型——>形参是实参的别名

    image-20230919130121037

  5. p++(后缀自增自减)优先级高于*p(解引用)

    cout << *p++ ; //先++再输出*p

  6. 通过函数来实现一种不太复杂的,并且要求加快执行速度,应选用内联函数

  7. 编译结果作为目标程序

  8. 条件编译:c语言允许有选择地对程序的某一部分进行编译或跳过

    作用:快速区分debug版、Linux版……

    image-20230919130141421

  9. c++语言本身没有输入输出语句,c++语言属于面向对象语言

  10. 高级语言程序的执行途径有两种:编译与解释。一般来说,编译比解释执行效率高

  11. 结构化程序设计的三种基本结构:顺序结构、选择结构、循环结构

  12. sizeof(int) = 4 sizeof(float) = 4

    sizeof(double) = 8 sizeof(char) = 1

  13. 仅由大写字母、小写字母、数字、下划线构成,并且首字符不是数字的字符序列都可以作为C++标识符。(×)不能是关键字

  14. 表达式 19 / 3 * sqrt(4) + 'A' 的数据类型为double,因为sqrt()函数返回的是double

  15. 定义数组时,可以用{}给数组元素赋值,可以不指定长度

    不使用 ={}赋初值时,static或全局数组均默认其为0或'\0',其他局部数组赋值随机

  16. c++中,数组不是对象,不用new去创建,定义的同时必须定义长度,在上分配空间

    若需要很大的数组,需要用指针配合new申请堆空间

  17. 用数组名作函数参数,数组名被认为是数组在内存中存放的首地址

  18. 定义指针类型时,必须指出它能表示何种类型程序实体的内存地址,或指向何种类型的程序实体

  19. 一个指针变量只能指向同一类型的变量,如int *p,p只能存放int数据的地址

  20. c++规定,数组名就是数组的起始地址,同时也是指针,也是0号元素的地址

  21. 使用delete释放c++类对象的内存时,会调用析构函数

  22. 引用在定义时必须初始化(除了作为形参的时候);

    1
    2
    int a; int &ra = a;
    void m(int &ra){}

    引用类型变量的初始化值不能是一个常数,除了const引用类型

  23. 声明对指针时不会调用构造函数,声明+赋值时会调用构造函数,只赋值时不会调用

  24. 函数的局部变量一般是在函数调用点自动创建和退出函数时自动消亡;普通变量的内存空间是在程序的静态数据区或栈区中分配的

  25. newmalloc的主要区别

    • new自动计算所需分配的空间大小,malloc需要显式指出

    • new自动返回相应类型的指针,malloc需要做显示类型转换

  26. 常指针&指针常量&指向常量的常指针

    int * const p = a 指针常量(指向不可改,指向的内存值可修改,只读限制)

    const int *p = &a; 常量指针(指针的指向可以修改,但指向的值不可修改

    const int const *p = &a; 指向常量的常指针,啥都不可以改

  27. 只能给枚举变量赋枚举值,若赋序号值必须进行强制类型转换

  28. 对一个指针不可多次使用delete运算符

  29. c++类的缺省函数:构造函数、拷贝构造函数、析构函数、赋值函数

    1
    2
    3
    4
    5
    6
    7
    A(void); // 缺省的无参数构造函数

    A(const A &a); // 缺省的拷贝构造函数

    ~A(void); // 缺省的析构函数

    A & operate =(const A &a); // 缺省的赋值函数
  30. 对象引用作为函数参数比对象指针更方便些

  31. 在说明语句int *f()中,标识符f代表一个返回值为指针型的函数名

  32. int** num; num = new int* [20]; new分配了长度为20的整数指针数组空间,并将num[0]的指针返回

  33. 指针通过地址间接访问某个变量,而引用通过别名直接访问某个变量

    引用一旦被初始化,不会再作为其它变量的别名

  34. 智能指针是一个栈对象,用于管理对象的堆内存,使用get()函数才能返回裸指针

    作用:管理对象的生命周期

  35. #include <xx.h> 到系统目录中寻找,不会搜索应用程序当前目录

    #include "文件名"先到当前cpp所在的目录定位,然后到系统目录中定位

    image-20230919130213073

    image-20230919130225903

  36. 构造函数可以被重载,析构函数不可以被重载

    构造函数可以有多个且可以带参数,而析构函数只能有一个不能带参数

  37. const是函数类型的一部分,在实现部分也要带该关键字。

    const关键字可以用于对重载函数的区分。

    常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。

    非常量对象也可以调用常成员函数,但是如果有重载的非常成员函数则会调用非常成员函数

  38. 动态联编 需要满足的两个条件:被调用的成员函数是虚函数、用指针或引用调用虚函数

  39. 对单目运算符重载为友元函数时,可以说明一个形参。而重载为成员函数时,不能显式说明形参。

  40. 可以使几个不同类型的变量共用同一段存储单元。(共用体

  41. 纯虚函数和虚基类是两个不同的概念!!!!!!!

    • 含有纯虚函数的类不能创建对象

    • 虚基类是指在继承的时候,通过虚继承的方式继承的基类

    • 含有纯虚函数的类在继承的时候,可以不加virtual关键字,因此可以不是虚基类

    • 只要在继承的时候,加了virtual关键字,都是虚基类,这与基类中是否有纯虚函数无关

  42. 在虚函数中可以使用this指针

  43. 析构函数可以是虚函数,构造函数不能

    虚析构函数的作用是:delete动态对象时释放资源

    构造函数不能是纯虚函数的原因:抽象类无法实例化对象,所以它不需要也不允许有构造函数

  44. C++语言中设置虚基类的目的是解决多继承造成的二义性问题

  45. 派生类的虚函数与基类的虚函数具有相同的参数个数和类型

  46. 动态绑定在运行时确定所调用的函数代码,重载函数是在编译时确定操作函数的

  47. 存在类继承并且析构函数中有必须要进行的操作时(如需要释放某些资源,或执行特定的函数)析构函数需要是虚函数,否则若使用父类指针指向子类对象,在delete时只会调用父类的析构函数,而不能调用子类的析构函数,从而造成内存泄露或达不到预期结果。

  48. 布尔型变量,位模式全0的视为false,其他都是true

    比如"""0"都是字符数组,地址非0,相当于true

  49. 一个下划线开头的标识符保留给系统用。比如:_WIN32

    两个下划线开头的标识符保留给语言用。比如:__cplusplus

  50. 全局对象会在进入main()之前构造好

    局部对象进入作用域后自动构造和析构,离开作用域自动析构

    静态对象执行到才构造,脱离main后才析构,没执行到就不会析构

  51. 局部对象按照声明的顺序构造,按逆序析构

    函数参数是从左往右依次构造的

  52. 把派生类对象赋给基类变量会导致“对象切割”,基类部分拷贝构造或赋值。

    公有继承表明派生类对象能当基类对象用,那么基类指针和引用都可以指向派生类对象。

    机制必须通过指针或引用才能体现,通过对象则不行(因为明确知道类型)

  53. 局部对象在函数栈上分配,函数栈是从高地址向低地址延伸