建立TCP客户端
使用 Socket 定义的 api 创建一个基本的客户端目录创建一个 socket
同客户端相同,使用 socket 函数返回一个 SOCKET 对象
返回值:SOCKET 对象
参数 1:IP 协议
参数 2:SOCKET 数据流种类
参数 3:网络协议
客户端可以不用声明网络协议,直接填写 NULL 也可以
连接服务器 connect
使用 connect 函数和服务端服务器进行连接
返回值:连接是否成功的状态
失败返回 0,建议使用宏定义
参数 1:SOCKET 对象
传入当前程序的 SOCKET 接口,也就是第一步创建的 socket 对象
参数 2:IP 数据信息
创建 sockaddr_in 对象,并且设置服务器的 ip 地址和端口号以及 IP 协议的信息
参数 3:IP 数据报的长度
传入 sizeof(IP 数据信息类型的长度)即可
接收服务器信息 recv
使用 recv 函数接受服务端返回的数据
返回值:返回数据的长度
同样的,当返回数据的长度为 NULL(0)时,说明未接收到信息
参数 1:当前进程的 S ...
建立TCP服务端
使用 Socket API 建立简易的 TCP 服务端建立一个 socket
使用 socket 函数返回SOCKET 对象
返回值:设置完成的 SOCKET 对象
参数 1:IP 协议类型
参数 2:传输的数据报类型
[^SOCK_STREAM]: 流格式 SOCKET
流格式套接字(Stream Sockets)也叫“面向连接的套接字”,在代码中使用 SOCK_STREAM 表示。
SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,则会重新发送。
流格式套接字有自己的纠错机制,在此我们就不讨论了。
SOCK_STREAM 有以下几个特征:
数据在传输过程中不会消失;
数据是按照顺序传输的;
数据的发送和接收不是同步的(不存在数据边界)
基于 TCP 协议
[^SOCK_DGRAM]: 数据报格式 SOCKET
数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。
计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另 ...
Variadic Templates(可变模板)
Variadic Templates(可变模板)基本语法注意下面三个[…]的位置不同
声明接收任意个数的参数
template<typename T, typename [1]... Types>
函数接收任意个数的参数
void func(T& fisrtArg, Types&[2]... args)
递归调用时传入任意个参数
func(args[3]...);
原理
参数个数
利用参数个数逐一递减的特性
实现递归函数的调用,使用**function template**完成
参数类型
利用参数个数逐一递减导致参数类型也逐一递减的特性
实现递归继承或递归复合,使用**class template**完成
注意点
模板接收任意参数时至少要有一个参数
所以在写递归模板时,需要写一个无参数的重载函数作为终止条件
1234567891011template<typename... Types>void func(Types... args){}//上一个函数可以和该函数同时存在//但下面的函数作为特化版本,会优先 ...
lambda表达式
lambda 表达式
基本语法
12345auto 函数名[可为匿名] = [使用外界的参数, &代表引用传递](传入的参数) (可选项)(mutable[代表值传递时是否可写])() ->(返回类型) { //函数体内容};
lambda 为创建匿名函数,需要用名字接收时,使用auto 关键字创建对象
需要使用 lambda 对象类型,使用**decltype(**lamabda 对象名)
可以省略返回类型,函数体内直接 return 返回值,由编译器自动推导
使用场景
临时想到需要创建函数时
需要临时重载小括号之类作为参数时
返回值类型
lambda + auto = 随时构建泛型函数在 C++14 下, lambda 支持使用 auto 自动推导参数, 那么也就可以使构造的 lambda 表达式具有泛型的能力
12345678910111213141516#include <iostream>#include <string>using std::cin;using st ...
右值引用
右值引用概念
左值
可以出现在 operator = 左边的
右值
只能出现在 operator = 右边的
右值不可取地址
常用的标准库函数
std::move()
该函数只有让左值绑定到右值引用的函数上的作用,除此之外,没有任何额外的功能
使用该函数并没有任何的作用,不产生任何优化
std::forward()
该函数目的是实现完美转交–即左值传递后仍然为左值,右值传递后仍然为右值
原因参照左值引用和右值引用均为左值, (也可以说是有名字的变量都为左值)
常见右值情况
a + b
临时对象
左值通过**std::move()**函数绑定为右值
使用场景
需要对右值单独进行优化,一般是使用浅拷贝
需要在右值引用的重载函数中单独实现
实现时需要注意传递后需要改变原变量的指向,否则右值传递后会立即销毁
如果还指向原变量内容,则原变量的内容就会被销毁,导致后续操作产生未定义行为
对使用标准库容器的效率进行优化
尤其对 vector 的效率优化效果明显
主要是因为 vector 在使用的过程中,会发生**成长**的行为
成长过程中,如 ...
内存管理--VC6之malloc
内存管理—VC6 之 mallocmalloc(C++程序)初始化操作_heap_init
对CRT 需要使用的内存进行初始化分配, 大小为 4k (第一次为 4k, 后续再申请, 获得的大小会自动增长)
命名为*crt_heap*
_sbh_heap_init() 从上一步获得的_crt_heap内存中, 分配 16 个**tagHeader** [^链表头节点]
[^链表头节点]: 即创建 16 张链表
设定__sbh_pHeaderList指向第一个链表头节点
为了对后续分配的内存完美管理 : 该 16 个链表对应管理分配的内存 (一个 Header 通常拥有 1MB 内存)
**设定__sbh_pHeaderDefer = NULL(nullptr), 用于记录全回收的 Header 的指针
tagHeader 数据结构12345678910typedef unsigned int BITVEC //因为uint的大小为32位,所以借用uint来表示32位的结构typedef struct tagHeader{ BITVEC bitvEntr ...
内存管理--简单内存池的实现
内存管理–简单内存池的实现
内存管理–简单内存池的实现
内存池的作用
内存池 1.0
[1.0]实现细节(易踩坑)
[1.0]成品代码
[1.0]缺点分析
内存池 2.0
union 联合体
[2.0]实现细节
[2.0]成品代码
[2.0]缺点分析
内存池 3.0
[3.0]实现细节
[3.0]成品代码
[3.0]缺点分析
内存池 4.0 - [4.0]实现细节 - [4.0]成品代码
内存池的作用
减少空间占用
使用 malloc 分配内存时 , malloc 会多分配一部分空间作为 cookie
cookie 的作用是记录分配的内存大小等信息, 方便 free 回收
C++中的::operator new(全局中的 new)和::operator free,其内部都是调用 malloc 或者 free
使用内存池, 可以让多次 new 申请的内存, 只需要分配 malloc 内存池时的 cookie 就可以了, 大大减少了 cookie 导致的内存浪费
减少频繁分配内存时的开销
自己实现内存池, 一次性分配较多的空间, 也就意味这多次 new 申请内存时, ...
内存管理准备知识
内存管理准备知识计算机系统关于内存的函数层级
graph TD
A(C + + 应 用 程 序)-->B(C++标准库分配器)
A-->C(C++关键字)
A-->D(C语言关键字)
A-->E(操作系统最底层api)
B-->C
C-->D
D-->E
编程者处于 C++应用程序部分
C++标准库分配器:std::allocator, ...
C++关键字:new, new[], new(), ::operator new(), ...
C 语言关键字:malloc / free
操作系统 api:HeapAlloc, VirtualAlloc, ...
内存管理--C++关键字
内存管理–C++关键字new
使用示例 : int* ptr = new int;
申请该对象大小的内存 **_(由 operator new)_**来完成
将内存绑定到声明的指针变量
通过该指针变量调用其构造函数 使用 new 时带有参数,则传递相应的参数
该操作(通过指针调用其构造函数) 只能由编译器调用
不可重载
new[]
使用示例 : int* ptr = new int[size];
申请 size * 对象大小的内存 **_(由 operator new)_**来完成
将内存绑定到声明的指针变量
通过该变量,调用 size次构造函数,传递时带有参数,则传递相应的参数
不可重载
operator new
使用示例 : int* temp = operator new(sizeof(int));
内部实际是调用 mallloc 函数
可以发生重载
new()
使用示例 : int* ptr2 = new(ptr)int
使用 new()表示**由程序员自己来决定地址空间的首地址**
指定的首地址在示例中,即 ptr 的地址
申请空间的大小为对应的类型 ...
内存管理--GNU2.9alloc
内存管理–GNU2.9::alloc
非标准版的分配器, 标准版分配器 std::allocator, 本质是直接调用 new 和 delete, 不进行任何优化
实现细节
内部维护16个链表, 分别对应 n 个字节 的请求
每次申请内存时, 申请20 个装入对应的链表中, 再额外申请 20 个对应大小 + 增长大小放入缓冲池
增长大小会最后将申请内存对齐成 16 的倍数
当申请新链表时, 先从缓冲池内分配内存, 当缓冲池的内存不足一个申请单位的时候,
将剩余内存放入对应的链表中, 再重新按照上一步申请内存
当可用内存不足(即无法再分配足够内存)时, 优先向比申请内存大的链表中取单个节点内存给调用者
如果缓冲池中有内存碎片,同上一步相同, 放入对应链表中后再进行操作
当比申请内存大的链表中找不到空闲的节点内存可供分配时, 返回分配内存失败
不会将申请内存细分, 为了其他程序考虑
不会将其他小的内存链表中取多个节点拼凑, 技术难度太大



