Learn_CPP www.learncpp.com
{
std::
cout
cin
<<endl的效率不高 -> 做了两件事情 1.移动光标到下一行。2. flush
}
Chapter4
{
4.6 定义系统变量位数
{
int -> 4byte
std::int8_t 1byte signed -> -128 ~ -127
std::uint8_t 1byte signed -> 0~255
int16_t 2byte
…
std::int32_t 4byte
std::int64_t 8byte
用于 xintx_t -> typedef long int int64_t
将所有类型进行区分,可以用于重定义。
如 在16bit 芯片中将int定义为16位的类型->typedef long int int16_t
int_fast#_t #= 8,16,32,64 -> 至少#位的数据类型
int_least#_t -> 最多#位的数据类型
最大#位 ,最小#位
size_t -> 当前系统中int 类型的位数
}
4.7 科学计数法
{
4.2030e4
}
4.8 浮点数据类型
{
123456789
123456792
在做bool运算时 bool a = 123456789.0f == 123456792.0f;
得到的结果为a = true. 原因是浮点数据类型仅精确到7位有效数字
float sig : 1 指数: 8bit 尾数: 23bit 单精度浮点型,和int一样占有4字节
double sig : 1 指数: 11bit 尾数:52bit 双精度浮点型。占有八个字节。
}
4.9
{
cout<<std::boolalpha; -》 print bool 以 true 和flase 的方式显示 。否则会输出01
cin>>b (bool b{}) 只能输入0 1 输入其它默认为0
}
4.11
{
在C ++中,固定宽度整数int8_t通常与带符号的char相同,因此通常将其打印为char而不是整数。
打印char 对应数字 ->
char b{97};
int a(b);
cout<< a <<endl;
静态转换 -> static_cast<new_type>(expression)
例子 : << static_cast<int>(b) << -> 静态转换。
example cin>>b 单次只能赋单个值,但是其余 值都会保存在键入缓存队列中。
直到调用完成。
int main()
{
char b{};
cin >> b;
cout << b << "\t has code \t" <<static_cast<int>(b) << endl;
cin >> b;
cout << b << "\t has code \t" << static_cast<int>(b) << endl;
cin >> b;
cout << b << "\t has code \t" << static_cast<int>(b) << endl;
cin >> b;
cout << b << "\t has code \t" << static_cast<int>(b) << endl;
}
结果
{
输入 abcd
输出 :abcd
a has code 97
b has code 98
c has code 99
d has code 100
}
\a 发出警报 -> 蜂鸣声
\b 向后移动光标一格
\f 将光标移动到下一个逻辑界面 ?
\n \r \t
\v 竖向跳格,不常用。
对单个字符'\r' 使用单引号而不是双引号,可以提高编译效率
\x() 以16进制显示 \x69 这里的69位16进制数
\o() 以8进制显示
char8_t char16_t char32_t
}
4.12 Literals 常量
{
C++ 包含两种常量 文本常量 和 符号常量
return 5; -> 这里的5 就是常量
true,false
添加类型后缀(suffix) 比如 5u -> 表示无符号的类型5
u || U -> unsigned int
l || L -> long
ul || UL -> unsigned long
ll || LL -> long long
ull || uLL -> unsigned long long
f || F -> float
l or L -> long double
在c++ 中没有后缀将其视为双精度文字
}
4.13 const 指定常量
{
const 指定的参数必须赋初值
const double pi{3.14159};
两种常量-> 运行时常量和编译常量
运行时常量 -> 程序运行前无法确认初值
{
int age{};
cin >> age;
const int constAge{age};
}
编译常量 -> 程序编译时已经确定初值
{
const int age{18};
}
常量通常作为函数参数传入
void DoSomething(const int a)
{}
C++ 11 -> 引入 constexpr 来指定编译时常量(必须赋确定值)
exp: constexpr double {9.8};
#define AnExampleValue 30
可以定义常量,但是尽量不要这么做。因为其作用域是全局的。
外部调用时可能出现命名重叠导致的编译错误。
定义全局常量的理想方法->
{
1.创建一个头文件来放这些常量
2.在这个头文件内部声明一个命名空间
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace constants
{
constexpr double pi{3.14159};
...
}
#endif
c++ 17 中 建议加入inline -> 提升性能
inline constexpr double pi{3.14159};
3.将所有常量放入命名空间中 确保c++编译版本。 如constexpr
4.引入头文件在需要的地方
}
}
5 运算操作符
{
5.3
{
乘幂pow()操作不包含在标准库中 必须包含 #include <cmath>
因为幂操作容易导致溢出
pow(3,4);
}
...
}
o. 位操作
{
#include <bitset> 需要包含位操作库
std::bitset<8> mybitset{};
mybitset.size() -> 8
mybitset.test(n(0到7)) 允许我们查询单个位
mybitset.set(n) 置位单个位
mybitset.flip(n) 将该位反转
mybitset.reset(n) 将单个位
注意置位时顺序位低位到高位
位操作运算符
<< 左移运算符
>> 右移运算符
~ 反转
&
|
^
c++ 20 以前不要移位有符号整型
cout (输入输出流)中的 << 重载移位<<的符号
位掩码-> 0100 & 1000(mask) 只运算需要的位。
位运算转为十进制
}
6. 对象
{
6.1 block (结构体语句) ------> {}
{
{}可以嵌套在函数内。 使用独立作用域
如 int main()
{
{
int a{5};
}
cout<<a; -> 非法。找不到a
}
}
6.2 命名作用域 namespace
{
namespace Mynamespace
{
...
}
使用作用域的方法:
::-> . Mynamespace:: ...
可以自己扩展命名空间,即库,但标准库std被禁止扩展。
命名域可嵌套
namespace a
{
namespace b
{
}
}
namespace a::b
{
}
可以定义作用域的别名来简化调用
int main()
{
namespace ab = a::b;
...
}
6.3 局部变量
{
}
6.4 全局变量
{
}
6.6 内联
{
内部全局变量 只在当前文件有效 static
static int a{};
const int b{}; //const默认为内联
constexpr int c{3}; //constexpr 默认为内联
非常量全局变量指定为内部变量的方法是加 static
这样这个变量就只对当前文件有效。
常量(const,constexpr)默认为内部变量
内部函数 函数名称前加 static
static int add(int ,int){}
}
6.7 外部函数
{
a.cpp
void Hello(){cout<<"Hello";}
main.cpp
#include "a.h" //包含a的头文件
void Hello(); ->首先声明该函数。
int main()
{
Hello();
return 0;
}
这种前向声明的方法。
!使用external 来声明外部变量
a.cpp
int a{3}; -> 非常量变量默认为可链接外部变量
external const int b{4}; -> 常量变量需要加external来声明为外部变量
main.cpp
external int a;
external const int b; 使用external变量的前向声明,声明完之后才能在本文件使用
!定义未初始化的非常量全局变量,不要使用external关键字,否则会认为正在对其进行前向声明
!函数声明不需要external声明
全局变量初始化在执行功能之前。
静态初始化和动态初始化,动态初始化?
变量初始化不能依赖于其他变量,
否则会出现变量值与预期不一定的现象。原因是静态初始化和动态初始化的问题。
尽可能避免动态初始化。
}
6.8 内联变量
{
多个文件调用,防止更改一次造成大量文件重新编译。
使用头文件,链接文件,和使用文件组成
注意只能使用const 因为 constexpr 无法进行前向声明。
constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace constants
{
external const double pi;
external const double gravity;
}
#endif
constants.cpp
#include "constants.h"
namespace constants
{
external const double pi{3.1415926};
external const double gravity{9.8};
}
main.cpp //使用
#include "constants.h"
int main()
{
cout<< 2.0*r*constants::pi<< endl;
}
这里多使用一个constants.cpp文件来链接。可以避免修改头文件constant参数而导致的大量文件重编译的情况。
但是这种方法已经过时。 在C++ 17以上的版本使用
使用inline 来定义内联变量 这样在同样实现了12的优点,避免了缺点
constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H
namespace constants
{
inline constexpr double pi{3.1415926};
inline constexpr double gravity{9.8};
}
#endif
main.cpp //使用
#include "constants.h"
int main()
{
cout<< 2.0*r*constants::pi<< endl;
}
}
6.10 static local 静态局部变量
{
在当前文件执行过程中声明的这个变量不会被摧毁且只会声明一次。
之后的操作也将一直保留,但只能被当前这个函数修改。
}
6.11 summary
{
Local variable int x; Block Automatic None
Static local variable static int s_x; Block Static None
Dynamic variable int *x { new int{} }; Block Dynamic None
Function parameter void foo(int x) Block Automatic None
External non-constant global variable int g_x; File Static External Initialized or uninitialized
Internal non-constant global variable static int g_x; File Static Internal Initialized or uninitialized
Internal constant global variable constexpr int g_x { 1 }; File Static Internal Must be initialized
External constant global variable extern constexpr int g_x { 1 }; File Static External Must be initialized
Inline constant global variable inline constexpr int g_x { 1 }; File Static External Must be initialized
Internal constant global variable const int g_x { 1 }; File Static Internal Must be initialized
External constant global variable extern const int g_x { 1 }; File Static External Must be initialized at definition
Inline constant global variable inline const int g_x { 1 }; File Static External Must be initialized
}
6.12 using
{
{
using ...
...
}
{
using ...
...
}
}
6.13 typedef
{
typedef double myDouble_t
typedef *poniter APoint
建议_t来表明这是一个反向的后缀。
也可以使用using来代替typedef 比如using myDouble_t = Double
}
6.14 auto
{
自动识别类型
auto d{5.0};
auto i{1+2};
auto sum{add(a,b)};
auto add(int x, int y) -> int
{
return (x + y);
}
c++20可以尝试的新写法?
void addAnd(auto a,auto b)
{
cout<< a+b;
}
}
6.15 隐式转换
{
double -> float 会造成低精度小数位发生误差。
int a = 3.5 -> a ==3
由于 一般不希望精度丢失,所以使用{} 进行初始化,
在这种初始化方式下不允许产生精度丢失。
int a{3.5f} 会报错
隐式转换允许升类型,不会造成精度丢失比如 int->float
The priority of operands is as follows:
long double (highest)
double
float
unsigned long long
long long
unsigned long
long
unsigned int
int (lowest)
}
6.16 显式转换 cast static_cast
{
5种转换方法: C-style, static_cast, consts casts ,dynamics cast,reinterpret cast
C-style
{
auto a{(int)(3 + 4.2)};
auto b{(float)3};
}
static_cast C++提供
{
char c{'a'};
cout << c << '' << static_cast<int>(c) << '\n';
比C强制转换弱,提供编译检查。 不能转换const等。
溢出不会警告,表明可以承担溢出的后果。
}
6.17 匿名命名空间 和内联命名空间
{
所有未命名的命名空间被认为是父命名空间的一部分。
但匿名命名空间中的信息只能在当前文件中被找到。
namespace
{
void doSomething() //只能在当前文件被找到。
{
cout<< "v1\n";
}
}
int main()
{
doSomething(); //无需声明命名空间
return 0;
}
使用内联命名空间 -> !默认命名空间。
{
inline namespace v1
{
void doSomething(){}
}
namespace v2
{
void doSomething(){}
}
int main()
{
v1:doSomething(); ->执行v1的
v2:doSomething(); ->执行v2的
doSomething(); ->执行v1的
}
通过这个可以实现修改某个函数并在新方法内通过调用v2::来执行v2的函数
而前面仍然使用v1的函数,实现开闭。
}
}
}
}
}
7.控制流 与错误处理
{
7.1 控制流语句
{
状况 if,switch
跳转 Goto
函数调用 Function()
循环 for,while,do while, !ranged-for
停止 sys::exit() sys::abort()
意外 try,throw,catch
}
7.6 Goto语句
{
int main()
{
double x{};
tryInput: //这是goto的标志位
cin>>x;
if(x < 0) goto tryInput;
cout << "out !!!" <<x<<endl;
return 0;
}
}
7.9 For
{
for(int i{0};i<length;i++)
{
...
}
}
7.17 std<<cerr -> 控制报错 assert
{
std::cerr << "Error : Could not divide by zero"
assert(gravity >0) 断言。 若断言出错则报错
NDEBUG 可以控制断言是否开启。
}
7.x Range for
{
vector<int> vecInt;
for (auto i : Vector)
{
cout<< i;
}
}
}
8. Compound Types ——> 复合类型
{
8.1 std::string
{
#需要包含string 库
#include <string>
std::string myName{"Donald"};
string 初始化若没有赋值,之后cin只能得到一个单词
https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdstring/
string str{};
此时不能用std::cin,改为使用
std::getline(std::cin,age),但使用时需要删除换行符,否则换行符会被认为改行已经结束。
int main()
{
int choice{};
std::cin >> choice;
std::cin.ignore(32767,'\n'); 忽略最多32767个字符直到删除'\n'
std::cout << "enter name" ;
std::string name{};
std::getline(std::cin,name);
std::cout << name << choice << std::endl;
return 0;
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
->忽略最大流输入的所有 直到删除\n。
string append
a + b
string 长度
a.length() -> 得到的是char 的长度。
}
8.2 Using a language reference {}
8.3 Enumerated 枚举类型
{
enum Color
{
color_black,
color_red,
color_green,
color_white,
...
}; ->枚举类需要加;结尾
使用->
Color paint{color_black};
枚举类型会默认赋值0,1,2,3,4,5,6,。。
}
8.4 Enum class 枚举类
{
由于枚举类型会赋初值,有可能出现不同枚举出现相同值而被系统认为相同的情况。
此时使用枚举类。
enum class Color
{
red,
blue
};
enum class Fruit
{
banana,
apple
};
Color color {color::red};
Fruit fruit{Fruit::banana};
color == fruit -> 报错。 系统无法比较不同枚举类
可以通过static_cast<int>(color)来强制将这个枚举类转换为整型。
}
8.5 结构体 Structs
{
struct Employee
{
int id{0};
int age{0};
double wage{0};
};
//使用
Employee joe{};
//Employee donald{};
joe.id = 14;
joe.age = 32;
joe.wage = 24.15;
Employee donald{1,24,500000.0};
Employee Donald{donald}; //为新的结构体复制一份原来的值。
函数也可以返回结构体。
Employee GetEmployed()
{
return {0,0,10000};
}
结构体可以嵌套结构体。
结构体的大小
sizeof(Employee)->
得到的大小为当前结构体的最大数据类型的大小,
比如这里会得到16 (double的大小)
}
8.6 !随机数生成器
{
生成随机数依赖于伪随机数生成器(PRNG)
包含一个起始数字(种子)。->经过运算 -> 生成随机数 -> 新种子。
种子一般只播种一次。
内置伪随机数生成器 -> 需要包含#include <cstdlib>
需要调用的函数 std::rand() std::srand()
int main()
{
std::srand(5253) //播种5323
想要实现高随机可以尝试将当前世界时间(基本不可重复)作为种子播种进去。
std::rand(); ->得到一个随机值,第一次调用不使用以便获得更好的随机效果。
for(int count{1};count <= 100; count++)
{
std::cout<< std::rand()<< endl;
}
}
使用系统注册时间播种->
std::srand(static_cast<unsigned int>(std::time(nullptr))); //nullptr 指向空的指针 == 0
<random>库中包含许多随机数生成器
{
#include <random>
#include <ctime>
int main()
{
//初始化种子
std::mt19937 mersense{static_cast<std::mt19937::result_type>(std::time(nullptr))}
//创建die随机数生成器
std::uniform_int_distribution die{1,6};
for(int count{1};count <= 48; ++count)
{
std::cout << die(mersenne) << '\t';
}
}
}
}
9 Arrays 数组
{
9.1 Arrays
{
int testScore[30] {};
int testScore[5]{1,3,57,2,3};
int testScore[5]{1,3,5}; //声明5个但只初始化3个,其他的会默认为0
}
9.2 Arrays
{
Arrays的长度 sizeof(Arr)/sizeof(Arr[0]);
std::size(Arr); // 需要 #include <iterator> // for std::size
->这个得到的是子元素个数
C++不会对数据Arr大小进行检查,若超过Index 将随机修改内存值!!
}
9.3 Arrays Loop
{
for(int i{0};i < std::size(scores); i++)
{
...
}
for (auto i : Arr) //int,float....
{
}
}
9.4 Sorting an array using selection sort
{
工作原理 -> std::swap(a,b)
https://www.learncpp.com/cpp-tutorial/sorting-an-array-using-selection-sort/
1.选择排序
使用<std::algorithm>命名空间 来调用std::sort()函数
std::sort(std::begin(Arr),std::end(Arr));
}
9.5 Multidimensional Array 多维数组
{
int Arr[3][4]; -> 3行4列数组
int Arr[3][5]
{
{1,2}, -> 1,2,0,0,0
{3,4,5}, -> 3,4,5,0,0
{6,7,8,9} -> 6,7,8,9,0
};
}
9.6 C-style string
{}
9.7 String_View
{
https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdstring_view/
c++ 17 -> string_view
String_View 提供一个可观测string(指向该地址,不另外声明空间),不允许修改。
陷阱-> string_view 末尾没有'\0' ! 使用时注意边界。
string_view 指向的字符串不能在string_view被销毁前被回收。
std::string_view text{"hello"} 。
}
9.8 Pointer
{
int x{5}; //
std::cout << x << "\n" ; ->5
std::cout << &x << "\n" ; ->0027FEA0 存放该值的内存地址
间接操作符 *
std::cout << *(&x) << "\n" -> 5 取地址存放的值
声明一个指针 int *Ptr{};
声明指针指向地址 int *Ptr{&x};
C++也不允许将文字内存地址转换为指针比如int *Ptr{0027FEA0} 非法
&x 返回的是一个包含操作数地址的指针 比如一个指向x的地址的Pointer
typeid(&x).name() -> int *
int* ptr{&x} 声明指针指向地址,指针值代表地址,*指针*Ptr代表取地址
cout<< ptr -> 0027FEA0
cout<< *ptr -> 5
}
9.9 null Pointer 空指针
{
声明空指针
float *ptr{0};
int *ptr1{NULL}; //使用Null 需要引用 #include <cstddef>
int main()
{
int* ptr{ 0 };
//int* ptr; 现在未初始化的指针使用会报错
if (ptr)
cout << "1" << endl;
else
cout << "2" << endl;
}
C++ 11 空指针-> nullptr
int *ptr{nullptr};
nullptr 可以隐式转换为任何指针类型,包括函数类型。
比如可以先声明nullptr给需要函数指针的函数,或者预置初始值。
!声明函数指针
{
void useFun(int x, int (*ptr)(int funParameter)) //重点在声明这里
//注意函数名本身就是指针 所以 可以直接声明int Fun(int funParameter)
//也可以int x, int (*Fun)(int funParameter)
//在这里(*Fun) == Fun
{
cout << (*ptr)(x) << endl; //这里使用了函数指针。调用时直接用函数名
}
int Fun(int x)
{
return (x + 3);
}
int main()
{
useFun(5, Fun); //函数名就是指针
}
}
nullptr_t 类型,也在#include <cstddef>
只能存放nullptr 无法转换。
}
9.10 Pointer And Array
{
https://www.learncpp.com/cpp-tutorial/pointers-and-arrays/
}
9.11 Pointer arithmetic and array indexing
{ 指针算术与索引
int value{7};
int* ptr{&value};
cout << ptr <<endl; ->value 内存地址
cout << ptr +1 <<endl; ->value 内存地址加4位
cout << ptr +2 <<endl; ->value 内存地址加8位
+多少位取决于 ptr的类型大小
对可迭代元素Arr,
使用std::begin() std::end()
可获得Arr的首尾地址。
}
9.12 C-style Pointer
{
const char* name{"donald"}; 赋值字符串数组?
char name1[]{"donald"};
这里声明的指针的地址不可变。
}
9.13 Dynamic memory allocation with new and delete
动态分配内存 allocation new delete
{
https://www.learncpp.com/cpp-tutorial/dynamic-memory-allocation-with-new-and-delete/
动态分配内存可以使用
new int 关键字
new int; -> 返回一个声明了一个int的地址。使用指针接收
//动态分配内存
int *ptr{new int};
//动态分配内存并初始化
int *ptr{new int(5)};
int *ptr{new int{5}};
!使用完以后记得删除
delete ptr;
ptr = nullptr; 指针指向0 //C++ 11 nullptr 以前 0
delete实际上并不删除,
但其告诉操作系统当前内存可以被覆盖了
此时ptr 成为悬挂指针,比较危险(可能不知道改了哪里的参数)
所以delete后 需要将ptr 指向空。
使用new (std::nothrow) int; 在分配内存失败时会指向空
这样可以防止程序报错。
int *ptr = new (std::nothrow) int;
!内存泄漏 (存在操作系统无法使用而本身程序也不再指向的内存地址)
{
int value = 5;
int *ptr{ new int{} }; // allocate memory
ptr = &value; // old address lost, memory leak results
!这里ptr造成了内存泄漏,声明的int没有delete。
}
}
9.14 Dynamically allocating arrays
动态分配数组
{
new []
delete []
{
int *Arr{new int[]{}};
delete[] array;
}
动态分配一个较大的内存空间
int length{10000000};
int *array{new int[static_cast<std::size_t>(length)]{}};
删除数组必须使用delete[] 否则会造成内存泄漏
动态分配并初始化
int *Arr{new int[5]{1,2,3,4,5}};
int *Arr{new int[5]{0}};
auto *Arr{new int[5]{1,2,3,4,5}};
}
9.15 Pointer and Const 常量
{
https://www.learncpp.com/cpp-tutorial/pointers-and-const/
很花哨的知识点。。用这么花真的好吗
非const 指针无法指向const 常量
const int x{5};
int *ptr{&x}; -> 编译错误
因此要指向常量就要建立常量指针。 const int*
const int x{5};
const int* ptr{&x}
但常量指针可以指向非常量
int x{5};
const int* ptr{&x}; ->合法
int* const ptr{&x}; //ptr将始终指向value
*ptr = 6; //允许,x为非常量,这里只限制地址。
//初始化后不允许任何修改,包括值和地址(只包括指针对其操作)
const int* const ptr{&x};
}
9.16 引用变量 类似于创建别名 &
{
int x{ 5 };
int& y{ x }; // y 引用x &x,&y输出一样的内存地址
注意const 值不能声明引用,因为引用会改真值
引用一般作为函数参数传入(如果需要更改的话)
注意如果参数包含常量,不能传入,否则会编译报错
void swap(int& a,int&b)
{
...
}
}
9.17 常量引用
{
}
9.18 成员选择与引用
{
使用.
struct Student
{
int age{};
double weight{};
};
Person person;
person.age = 5 ;
创建指向结构的指针,获取对象使用->
Person* ptr{&person};
ptr->age = 15; //和 (*ptr).age 作用一样
}
9.19 foreach循环 C++ 11
{
for(auto i : Arr)
{
}
需要更改其值 声明引用
for(auto& i : Arr)
{
}
foreach不能遍历指针数组const int array[]与动态数组
c++20
{
可以添加索引了
for(int index{0};auto i : Arr) //index 为当前索引
{
}
}
}
9.20 Void指针
{
#http://c.biancheng.net/view/429.html
void* void_ptr;
void 类型指针可以指向任何对象。
但这个指针不知道具体是什么类型,所以使用需要进行强制转换。
int* int_ptr{static_cast<int*>(void_ptr)};
}
9.21 指向指针和多维数组指针
{
可选
指向指针的指针
int **ptrptr = &ptr;
动态分配数组指针
int **Arr = new int*[10];
二维 ->
int (*Arr)[5] = new int*[10][5];
或者
auto Arr1 = new int[10][5];
}
9.22 std::array std::标准库中
{
#include <array>
std::array<int,3> Arr;
C++ 17(提供省略写法) -> std::array Arr;
访问允许使用下标运算符[],但其不进行边界检查。
使用Arr.at(5) 会进行边界检查
Arr.size()
函数使用时传入const std::array<int,5> &myArray
->防止其复制一遍
for (int i{ 0 };i < Arr.size(); i++)
//这里有错误,因为有符号不匹配。
//i的类型不对。
应该改为
for (std::array<int,5>::size_type i{ 0 };i < Arr.size(); i++)
或者使用auto自己找
for (auto {Arr.size() - 1};i >= 0; i--)
-> 但这样会存在死循环。(比如数组为0)
可以再改进加入限制条件等
或者使用foreach 索引
for(auto i : Arr)
{...}
}
函数参数传递 ->
值传递 Fun(A a) ->适合通用类型,会复制,如果有构造和析构会耗费性能
引用传递 Fun(A& a) -> 会改变值,不会复制提高性能
const 引用传递 Fun(const A& a) 不会复制提高性能,不能改值
9.23 std::vector 动态数组
{
#include<vector>
std::vector<int> Arr{1,3,42};
Arr.size();
Arr.resize(5); 将Arr大小放大为5
}
9.24 iterator 迭代器
{
}
9.25 standard library algorithm 标准算法库
{
#include <algorithm>
1. std::find
{
auto Found{std::find(Arr.begin(),Arr.end(),search)}
——>得到的Found是一个迭代器,其指向我们找到的元素
->没有则指向最后一个元素Arr.end() ->内存地址
}
2. std::find_if(Arr.begin(), Arr.end(), FindFun)
{
我们提供条件的bool函数FindFun,
——>得到的Found是一个迭代器,其指向我们找到的元素
->没有则指向最后一个元素Arr.end() ->内存地址
}
3. std::count 计数
{
count(Arr.begin(), Arr.end(),Num)
}
4. std::count_if
{
count_if(Arr.begin(), Arr.end(),CountFun) //CountFun : bool
}
5. std::sort
{
sort(Arr.begin(), Arr.end()); ->升序
sort(Arr.begin(), Arr.end()).std::reverse(); ->降序
sort(Arr.begin(), Arr.end(),SortFun); //SortFun : bool true-> change
}
6. std::foreach
{
foreach(Arr.begin(), Arr.end());
}
7. 可以保证顺序执行的算法
{
std::for_each()
std::copy()
std::copy_backward()
std::move()
std::move_backward()
}
C++ 20 添加了 ranges 取代 std::begin 和 std::end
}
}
10.Function
10.1
{
函数参数与参数
形参 parameter
实参 argument
}
10.2
{}
10.3 引用传递
{
void Fun(A &a);
void Fun (const A& a);
可以在函数参数命名时带Out 如 int& cosOut 来说明这个函数会改变这个值
}
10.4 地址传递
{
函数可以更改指针从而使其指向新的值或者nullptr
void Fun(int *ptr)
{
*ptr = 3; //更改值
ptr = nullptr; //更改指向地址
}
传递数组时必须把数组的长度作为参数传入,因为
数组名会变换为指针传入
Void Fun(int *Arr,int length)
{
if(!Arr) return; //空指针检验
}
传递常量地址
void Fun(const int* Arr,int length)
{}
传递指针时其实是把地址作值传递
void Fun(int *ptr)
{
*ptr = 3; //更改地址所指向的值-> 会在全局修改
ptr = nullptr; //更改指向地址 -> ptr时形参地址,所以改的地址只在本函数有效
}
例子
int five{ 5 };
int* ptr{ &five };
std::cout << ptr << endl; //-> 003FFD1C
Fun(ptr);
std::cout << ptr << endl; //-> 003FFD1C
创建可以在函数内部修改指针地址的函数 -> 传递引用指针
Void Fun(int *& ptr)
{
ptr = nullptr; //会全局更改
}
fun (int x) -> 值传递
fun (int &x) -> 引用传递
fun (int *x) ->地址传递
}
10.5{}
10.6 inline function 内联函数
{
对于小巧函数(内部实现简单功能) -> 大量函数调用增加了性能开销,
使用inline函数 可以使编译器在编译该函数时就地展开,编译的代码会更大。
inline int Fun(a,b)
{
return a>b? a:b;
}
inline 仅仅是建议,编译器考虑函数不适合内联并不会对其进行内联操作
}
10.7 函数重载
{
}
10.8 默认参数
{
void Fun(int a = 3)
{}
}
10.9 函数指针
{
int Fun1()
{}
int Fun2()
{}
int main()
{
int *(funPtr){&Fun1}; 函数指针指向Fun1
funPtr = &Fun2; 函数指针改为指向Fun2
return 0;
}
}
}
}