20230826日志
2023年8月26日
caution #
-
int guess(12)
与int guess{12}
的区别在C++中,这两种方式的区别是:
int guess(32); 是使用构造函数初始化 int guess{32}; 是使用统一初始化语法 构造函数初始化方式调用int的构造函数,使用括号中的值32来初始化guess。
统一初始化语法不需要调用构造函数,直接使用大括号{}初始化guess为32。
两者的结果是相同的,都将guess初始化为整数32。
主要区别在于:
- 构造函数初始化只适用于有构造函数的类类型,而统一初始化语法可以用于任何类型。
- 统一初始化语法可以防止隐式类型转换。例如int guess(3.14)会把3.14转为3,而int guess{3.14}在编译时会报错。
- 统一初始化语法在某些情况下会进行值初始化(将变量初始化为类型的默认值),构造函数初始化则总是直接初始化。
- 统一初始化语法可以初始化数组和聚合类型。
所以通常来说,统一初始化语法更为通用和安全,C++11后推荐使用统一初始化语法,而不是构造函数初始化
-
强制类型转化
(type) expression
type(expression)
static_cast<type>(expression)
-
auto 含义转变
- C 中表示内存自动分配释放
- C++ 中表示类型自动推导
-
基本类型
-
整型从小到大, bool, char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long
-
short 至少16位,int至少和short一样长, long至少32位,且至少和int一样长
-
浮点从小到大, float, double, long double; float至少32位, double至少64位, long double通常80~128位
-
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等,用于处理宽字符字符串。
通常使用宽字符可以避免编码问题,但缺点是占用空间较大
-
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)" 你好呀)+*";
-
熟练的使用cin
cin >> replace.x >> replace.y
-
-
数组
-
空/部分初始化,其余位置初始化为0
-
列表初始化禁止缩窄初始化
long plifs[] = {25, 92, 3.8}; // not allowed
-
-
string
- char arr[20]
- cin 通过空白判断输入结束
- cin.getline, cin.get 可以读取整行,对\n的处理不同; cin.get读取空行会阻断后续cin.get, 使用cin.clear()清除错误标志
- 数字字符混合输入
(cin >> year).get()
读取换行符 - 未初始化的char数组,
strlen(arr)
长度是不确定的, 可能长度会超过20
- string
- 读取整行
getline(cin, str);
- 读取整行
- char arr[20]
-
struct
- c++什么struct变量可以 省略struct
function #
-
原型
void say_hello(...)
与有可变参数的C函数交互时才这样做
-
实参 argument, 形参 parameter
-
指针与const
-
除了不可以将const地址赋值给常规指针,其他三种语法上都是合法的
-
如果数据类型本身不是指针,则可以将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指针
-
-
二维数组函数参数声明
int sum(int (*arr)[4], int size); int sum(int arr[][4], int size);
-
结构作为函数参数
- 按值传递,传递结构的副本
- 旧知识: 结构可以赋值给另一个结构
-
string作为函数参数
- string与结构类似
-
array作为函数参数
void show_array(std::array<double, 4> arr); // 按值传递,创建新的array对象,并拷贝其中的值 void fill_array(std::array<double, 4> *arr); // 更复杂, 需要像这样 (*arr)[0] 这样使用,引用可以解决这方面的问题
-
函数作为函数参数
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
-
引用
int x = 3; int cube(int& a); cube(x + 2); // invalid, 有些情况会创建临时变量 使用x+2的值初始化
-
临时变量
- 左值(左值参数):可被引用的数据对象
- 变量
- 数组元素
- 结构成员
- 引用
- 解除引用的指针
- 非左值
- 字面常量
- 包含多项式的表达式
- 创建临时变量的情况
- 实参类型正确,但不是左值
- 实参类型不正确,但可转为正确的类型
- 临时变量参数会阻止变量进行的修改, 现代编译器会报错
- 临时变量只读不修改值时,通过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; }
- 左值(左值参数):可被引用的数据对象
-
右值引用(通过&&定义)
double j = 15.0; double && jref = 2.0 * j + 18.5;
- 什么是右值:不能通过地址访问的值
- 字面值 e.g. 10.0
- 表达式的值 e.g. x + 10
-
返回引用的注意事项
- 引用赋值给指针会拷贝数据
free_throws * pt
会创建一个无名结构,然后pt指向它(相当于new, 需要delete释放)- 返回
const free_throws &
类型, 避免修改返回值
const free_throws & clone(free_throws & ft){ free_throws * pt; *pt = ft; // copy info return *pt; }
-
继承, 基类引用可以指向派生对象,无需强转
-
-
point or reference or basic point_or_ref
-
参数默认值
- 参数默认值通过原型声明
- 默认值参数必须放在非默认右侧
-
函数重载
- 类型与类型引用被视为相同的函数特征标
- 同时有多个可选的函数时,匹配最合适的函数
-
编译器如何选择原型
- 有多个完全比配时,指向非const数据的指针和引用优先与非const指针和引用参数匹配
-
对象数组
- 底层流程: 先使用默认构造函数创建对象,在将实例化的对象拷贝到之前构造的对象上
-
类中定义常量
class Stock { enum {Months=12}; double costs[Months]; } class Hobbies { static const int Months = 12; double costs[Months]; }
-
作用域内枚举
enum class A {a=1, b=2, c=3}; // 或者用struct 代替 class enum class B {a=4, b=5, c=6}; A m = A::a;
- 作用域内枚举不能隐式转换为int, 需要强转
int(A::a)
- 指定枚举底层类型
enum class :short pizza{Small, Medium, Large, Xlarge}
- 作用域内枚举不能隐式转换为int, 需要强转
-
复制构造函数和赋值运算符
- 示例
StringBad headline("Celery"); StringBad metoo = headline;
两种实现方式: 使用复制构造函数;先使用复制构造函数, 然后赋值运算符 when_copy_constructor
StringBad headline("Celery"); StringBad knot; // first default constuctor knot = headline; // then assignment operator
赋值运算符
- 赋值运算符不同点
- 目标对象可能引用了以前分配的数据,应使用delete释放该内存
- 避免将对象赋值给自身; 否则,给对象赋值前,释放内存操作可能删除对象的内容
- 返回指向调用对象的引用
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; }
赋值运算符只能由类成员函数重载
-
空指针
char * a = nullptr;
-
c++会区分常量与非常量的特征标
-
常量对象必须定义相应的[]常量方法, 才可以[]读取, 否则编译器无法确认是否会修改数据
-
静态类成员函数
-
声明需要static, 定义不需要
-
只能通过 String::HowMany(); 这样的方式调用
-
定义中不能使用成员变量,除了static的变量,不能使用this