20230826日志

20230826日志

2023年8月26日
2023
202308, C++

caution #

  1. int guess(12)int guess{12} 的区别

    在C++中,这两种方式的区别是:

    int guess(32); 是使用构造函数初始化 int guess{32}; 是使用统一初始化语法 构造函数初始化方式调用int的构造函数,使用括号中的值32来初始化guess。

    统一初始化语法不需要调用构造函数,直接使用大括号{}初始化guess为32。

    两者的结果是相同的,都将guess初始化为整数32。

    主要区别在于:

    1. 构造函数初始化只适用于有构造函数的类类型,而统一初始化语法可以用于任何类型。
    2. 统一初始化语法可以防止隐式类型转换。例如int guess(3.14)会把3.14转为3,而int guess{3.14}在编译时会报错。
    3. 统一初始化语法在某些情况下会进行值初始化(将变量初始化为类型的默认值),构造函数初始化则总是直接初始化。
    4. 统一初始化语法可以初始化数组和聚合类型。

    所以通常来说,统一初始化语法更为通用和安全,C++11后推荐使用统一初始化语法,而不是构造函数初始化

  2. 强制类型转化

    1. (type) expression
    2. type(expression)
    3. static_cast<type>(expression)
  3. auto 含义转变

    1. C 中表示内存自动分配释放
    2. C++ 中表示类型自动推导
  4. 基本类型

    1. 整型从小到大, bool, char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long

    2. short 至少16位,int至少和short一样长, long至少32位,且至少和int一样长

    3. 浮点从小到大, float, double, long double; float至少32位, double至少64位, long double通常80~128位

    4. wchar_t, 与char类型不同,wchar_t类型的大小是与平台相关的,通常为16位或32位

      它存储的字符,一般是当前系统或环境所使用的宽字符集中的一个字符,如中文系统就是宽字符中文。

      wchar_t类型所占空间大小实现相关,但不小于char类型,通常为2字节或4字节。

      wchar_t类型专门处理宽字符,当需要处理中文、日文或者其他宽字符时,需要使用wchar_t。

      C++字符串字面值L"Hello"就是宽字符串,存储字符类型是wchar_t。

      函数库提供了许多wchar_t版本的函数,如wcscpy、wcscmp等,用于处理宽字符字符串。

      通常使用宽字符可以避免编码问题,但缺点是占用空间较大

    5. chat16_t e.g. char16_t c1 = u’A';

          const char* a = u8"你好";
          const wchar_t* b = L"中国";
          char16_t a = u"你好";
          char32_t b = U"你好";
          string c = R"("hello"\n world, 你好呀)";
          // 原生字符串可以在 `"(` 之间添加字符,在 `)"` 之间也要包含相同的字符, 但是空格, 括号,斜杠,控制字符除外
          string info = R"+*("hello"\n world, "(haha)" 你好呀)+*";
      
    6. 熟练的使用cin

      1. cin >> replace.x >> replace.y
  5. 数组

    1. 空/部分初始化,其余位置初始化为0

    2. 列表初始化禁止缩窄初始化

        long plifs[] = {25, 92, 3.8};  // not allowed
      
  6. string

    1. char arr[20]
      1. cin 通过空白判断输入结束
      2. cin.getline, cin.get 可以读取整行,对\n的处理不同; cin.get读取空行会阻断后续cin.get, 使用cin.clear()清除错误标志
      3. 数字字符混合输入 (cin >> year).get() 读取换行符
      4. 未初始化的char数组,strlen(arr) 长度是不确定的, 可能长度会超过20
    2. string
      1. 读取整行 getline(cin, str);
  7. struct

    1. c++什么struct变量可以 省略struct

function #

  1. 原型

    1. void say_hello(...) 与有可变参数的C函数交互时才这样做
  2. 实参 argument, 形参 parameter

  3. 指针与const

    1. 除了不可以将const地址赋值给常规指针,其他三种语法上都是合法的

    2. 如果数据类型本身不是指针,则可以将const数据或非const数据的地址赋值给指向const的指针,但只能将非const数据的地址赋值给非const指针

      int age = 39;
      const int* pt = &age; // 这样的声明不意味着 pt指向的值是个常量,而是指对pt而言,这个值是常量
      *pat = 20;  // invalid
      age = 20;  // valid
      int sage = 80;
      pt = &sage // valid 仍然不能修改pt指向的值
      
      const int* finter // a pointer to const int
      int* const finter // a const pointer to int, 可以修改指针指向的值
      
      const float g_earth = 9.8;
      const float *pe = &g_earth;  // valid
      
      const float g_moon = 1.63;
      const float *pm = &g_moon;  // invalid
      
      int height = 173;
      int* pt = &height;
      const int* pe = pt;  // 一级间接关系: *pe = 180 invalid
      
      const int** p2;
      int* p1;
      p2 = &p1;  // invalid 二级间接关系: 不能将常量指针赋值给const指针
      
  4. 二维数组函数参数声明

    int sum(int (*arr)[4], int size);
    int sum(int arr[][4], int size);
    
  5. 结构作为函数参数

    1. 按值传递,传递结构的副本
    2. 旧知识: 结构可以赋值给另一个结构
  6. string作为函数参数

    1. string与结构类似
  7. array作为函数参数

    void show_array(std::array<double, 4> arr);  // 按值传递,创建新的array对象,并拷贝其中的值
    void fill_array(std::array<double, 4> *arr);  // 更复杂, 需要像这样 (*arr)[0] 这样使用,引用可以解决这方面的问题
    
  8. 函数作为函数参数

    double pam(int);
    double (*pt)(int);
    pt = pam;
    double x = pam(10);
    double y = (*pt)(15);
    
    double z = pt(16);  // also call pam using the pointer pt
    
  9. 引用

    int x = 3;
    int cube(int& a);
    cube(x + 2);  // invalid, 有些情况会创建临时变量 使用x+2的值初始化
    
    1. 临时变量

      1. 左值(左值参数):可被引用的数据对象
        • 变量
        • 数组元素
        • 结构成员
        • 引用
        • 解除引用的指针
      2. 非左值
        • 字面常量
        • 包含多项式的表达式
      3. 创建临时变量的情况
        • 实参类型正确,但不是左值
        • 实参类型不正确,但可转为正确的类型
      4. 临时变量参数会阻止变量进行的修改, 现代编译器会报错
        • 临时变量只读不修改值时,通过const创建的临时变量使函数更通用
      #include <iostream>
      
      void swaper(int &a, int &b);
      
      int cuber(const int &a);
      
      
      int main() {
         using namespace std;
         long a = 1, b = 2;
         // swaper(a, b);  // invalid
         int cube = cuber(a);
      
         cout << a << " " << b << "cube:" << cube << endl;
         return 0;
      }
      
      void swaper(int &a, int &b) {
         int temp;
         temp = a;
         a = b;
         b = temp;
      }
      
      int cuber(const int &a) {
         return a * a * a;
      }
      
    2. 右值引用(通过&&定义)

      double j = 15.0;
      double && jref = 2.0 * j + 18.5;
      
      1. 什么是右值:不能通过地址访问的值
      2. 字面值 e.g. 10.0
      3. 表达式的值 e.g. x + 10
    3. 返回引用的注意事项

      1. 引用赋值给指针会拷贝数据
      2. free_throws * pt 会创建一个无名结构,然后pt指向它(相当于new, 需要delete释放)
      3. 返回 const free_throws & 类型, 避免修改返回值
      const free_throws & clone(free_throws & ft){
         free_throws * pt;
         *pt = ft;  // copy info
         return *pt;
      }
      
    4. 继承, 基类引用可以指向派生对象,无需强转

  10. point or reference or basic point_or_ref

  11. 参数默认值

    1. 参数默认值通过原型声明
    2. 默认值参数必须放在非默认右侧
  12. 函数重载

    1. 类型与类型引用被视为相同的函数特征标
    2. 同时有多个可选的函数时,匹配最合适的函数
  13. 编译器如何选择原型

    1. 有多个完全比配时,指向非const数据的指针和引用优先与非const指针和引用参数匹配
  14. 对象数组

    1. 底层流程: 先使用默认构造函数创建对象,在将实例化的对象拷贝到之前构造的对象上
  15. 类中定义常量

    class Stock {
       enum {Months=12};
       double costs[Months];
    }
    
    class Hobbies {
       static const int Months = 12;
       double costs[Months];
    }
    
  16. 作用域内枚举

    enum class A {a=1, b=2, c=3};  // 或者用struct 代替 class
    enum class B {a=4, b=5, c=6};
    A m = A::a;
    
    1. 作用域内枚举不能隐式转换为int, 需要强转int(A::a)
    2. 指定枚举底层类型enum class :short pizza{Small, Medium, Large, Xlarge}
  17. 复制构造函数和赋值运算符

    1. 示例
    StringBad headline("Celery");
    StringBad metoo = headline;
    

    两种实现方式: 使用复制构造函数;先使用复制构造函数, 然后赋值运算符 when_copy_constructor

    StringBad headline("Celery");
    StringBad knot; // first default constuctor
    knot = headline; // then assignment operator
    

    赋值运算符

    1. 赋值运算符不同点
      1. 目标对象可能引用了以前分配的数据,应使用delete释放该内存
      2. 避免将对象赋值给自身; 否则,给对象赋值前,释放内存操作可能删除对象的内容
      3. 返回指向调用对象的引用
    StringBad & StringBad::operator=(const StringBad & st){
       if(this == &st){
          return *this;
       }
       delete [] str; // 释放this上旧的内存
       len = st.len;
       str = new char[len + 1];
       std::strcpy(str, st.str);
       return *this;
    }
    

    赋值运算符只能由类成员函数重载

  18. 空指针

    char * a = nullptr;
    
  19. c++会区分常量与非常量的特征标

  20. 常量对象必须定义相应的[]常量方法, 才可以[]读取, 否则编译器无法确认是否会修改数据

  21. 静态类成员函数

  22. 声明需要static, 定义不需要

  23. 只能通过 String::HowMany(); 这样的方式调用

  24. 定义中不能使用成员变量,除了static的变量,不能使用this