接上篇 [C++ 笔记] C 语法扩展, 内容太多就分成两篇了。(顺带可以多水一篇文章)
# 内存四区
- 栈 Stack:编译器自动分配释放
- 堆 Heap:程序员手动分配释放,结束时 OS 回收未释放的部分
- 全局区 / 静态区 Global / Static:全局 / 静态变量,程序结束时释放
- 常量区 Const:不可修改
# 常量与指针
# 常量
声明: const Type const_name = value 其中 const 与 Type 可互换位置。
ex: const char* STR = "Hello";
不加 const 会报错 *(试出来是 warning?)*,因为 "Hello" 存放在常量区,指针要指向常量区。(可以看下面 qwq)
# 指针
开始念咒.... 常量指针,指针常量,常量指针常量... 阿巴阿巴 (º﹃º)
其实就是 const 离谁近修饰谁:
// 指针 | |
int x = 5; | |
int *p = &x; | |
// 常量指针 (常指针) | |
const int x = 5; | |
const int* p = &x; //const 修饰 int* | |
// 指针常量 | |
int x = 5; | |
int* const p = &x; //const 修饰 p | |
// 常量指针常量 | |
const int x = 5; // 似乎不写 const 也可 | |
const int* const p = &x; // 不能通过 * p 修改 x 的值,且不能修改 p 的值 | |
int x = 2; | |
auto p1 = &x; //p1 类型 int* | |
auto p2 = "qwq"; //p2 类型 const char* | |
auto const p3 = "owo"; //p3 类型 const char* const |
# 各种宏定义
#define: 仅用于替换
#define macro_name sth.
typedef: 创建代替类型名的别名
Typedef Type NewTypeName
using 替代 typedef:
using identifier = type-id
只能用于类型,
using in = std::cin
报错,因为 cin 是类。实例:
using ull = unsigned long long;
ull x = 233u;
using Fptr = void(*)(int, int); // 函数指针
void example(int, int){...};
Fptr fp = example;
定义模板的别名,只能使用 using
# 变量作用域
# 局部作用域
文件作用域、函数作用域以及函数内部的块作用域。
就近原则:作用域不同的同名变量,优先使用作用域小的。
实例:
#include <iostream> | |
using namespace std; | |
int a = 3; | |
int main() | |
{ | |
int a = 2; | |
cout << a << endl; // 2 | |
return 0; | |
} |
# 一元作用域解析运算符
局部变量与全局变量同名时,使用 ::
访问全局变量
实例:
#include <iostream> | |
using namespace std; | |
int a = 3; | |
int main() | |
{ | |
int a = 2; | |
cout << a << ' ' << ::a << endl; // 2, 3 | |
return 0; | |
} |
# 函数相关
# 重载函数 Overloading Funcitons
假设已定义函数 int mx(int, int)
再定义 double mx(double, double)
(一个同名不同参的函数),调用函数 mx(v1, v2)
时:
编译器仅通过参数来匹配重载函数的调用,优先级:
- 参数个数
- 参数类型
- 不同类型参数顺序
实例:
#include <iostream> | |
using namespace std; | |
int mx(int num1, int num2) | |
{ | |
return num1 > num2 ? num1 : num2; | |
} | |
double mx(double num1, double num2) | |
{ | |
return num1 < num2 ? num1 : num2; | |
} | |
int main() | |
{ | |
cout << mx(2, 3) << endl; //3 | |
cout << mx(2.0, 3.0) << endl; //2 | |
cout << mx(2.0, 3) << endl; // 报错 | |
return 0; | |
} |
二义调用会导致编译错误,例如 int fun(int, double)
与 double fun(double, int)
,调用 fun(1, 2)
时会报错。又例如同时声明 int fun(int, int)
与 void fun(int, int)
编译器报错。
# 带有默认参数值的函数
Type Fun(v1, v2, v3 = , v4 = )
之类的写法。例如左边,调用时可传递 2 ~ 4 个参数,未接收实参的形参有默认值。
需要注意的是带有默认值的参数在右边,传递实参时依次传给左边。
实例:
#include <iostream> | |
using namespace std; | |
int mx(int a, int b = 3) | |
{ | |
return a > b ? a : b; | |
} | |
int main() | |
{ | |
cout << mx(2) << endl; // 3 | |
cout << mx(2, 4) << endl; // 4 | |
return 0; | |
} |
# 默认参数与重载
函数重载时,不允许重定义默认参数
以下实例均为错误:
double area(double r = 1){...} | |
double area(int r = 2){...} | |
---- | |
int add(int x, int y = 3) | |
{ | |
return x + y; | |
} | |
int add(int x) | |
{ | |
return x + 3; | |
} | |
add(x); |
反正一般编译器搞不定的这种问题都会报错 qwq
# 内联函数 inline
一句话:用空间换取时间。
声明方法: inline Fun(...){...}
, 内联函数定义与声明一般不分开。
仅适用于频繁调用的短函数,是对编译器的建议,并不一定都会进行内联编译 (循环,递归,静态变量等)。
# 函数后加 const
引用一段话:原文链接
我们定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是 "只读" 函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上 const 关键字进行标识,显然,可提高程序的可读性。其实,它还能提高程序的可靠性,已定义成 const 的成员函数,一旦企图修改数据成员的值,则编译器按错误处理。
# 基于范围的 for 循环
python 党狂喜文明进化的象征!😆😆😆
语法: for (元素名变量 : 广义集合) {循环体}
广义集合例子:
auto a1[]{1, 3, 5, 7}; // 不再推荐使用原始数组 | |
std::array <int, 4> a2{2, 4, 6, 8}; | |
std::vector <int> v = {2, 3, 3}; | |
std::vector <std::string> s{"qwq", "owo"}; |
要对广义集合里的元素进行操作,需要使用引用。
实例:打印数组 a 元素并将其翻倍。
int a[]{2, 3, 3}; // 写 auto 会报错 qwq | |
for (auto i : a) | |
cout << a << endl; | |
for (auto& i : a) | |
i *= 2; |
# 带有初始化器的 if 和 switch [C++17]
熟悉的初始化器: for (initializer; condition ; increment)
然后 if
与 switch
也能用: if (initializer; condition)
switch(initializer; condition)
实例:
int main() | |
{ | |
// 比起原先在外面定义 x,改变了作用域 | |
if (auto x = fun(233); x > 123) | |
{ | |
//do something with x | |
} | |
else | |
{ | |
//do something with x | |
} | |
auto x = 233; // 此处变量名 x 可重用 | |
} |
# 常量表达式
在编译期就可以计算值的表达式,由编译器计算。
const 修饰的对象未必是编译期常量,例如: const int MAXN = n;
使用 constexpr 关键字声明常量表达式。(C++11)
声明数组时,大小必须是常量表达式。
区别:
const | constexpr |
---|---|
告知程序员 被修饰变量不能修改,避免 bug。 | 修饰的表达式可以在编译期计算得到值,用于性能优化。 |
# 断言与静态断言
# 断言
检测假设成立与否的语句,不成立时提出警告信息。
assert 关键字:C 语言的宏,运行时检测。(不是函数,下 static_assert 同)
#include <cassert>
以调试模式编译程序。assert(bool_expr)
表达式为假则中断程序,报错。- 帮助解决逻辑错误。 (GDB 不香吗 qwq -> assert 可以在写程序时,在潜在的问题上写一下 assert)
例如: assert((i > 0) && "i must be positive");
# 静态断言
static_assert:几乎用不上,为写库的开发带师准备。 编译时断言检查。
用法: static_assert (bool_constexpr, message)
可转为 bool 的编译期常量表达式,报错信息。
大学四年真的会用到这东西吗 QwQ?