【C++基础知识学习笔记】精华版(复习专用)
常用语法
函数重载(Overload)
规则:
-
函数名相同
-
参数个数不同、参数类型不同、参数顺序不同
注意:
-
返回值类型与函数重载无关
-
调用函数时,实参的隐式类型转换可能会产生二义性
默认参数
C++ 允许函数设置默认参数,在调用时可以根据情况省略实参。规则如下:
- 默认参数只能按照从右到左的顺序
- 如果函数同时有声明、实现,默认参数只能放在函数声明中
- 默认参数的值可以是常量、全局符号(全局变量、函数名)
如果函数的实参经常是同一个值,可以考虑使用默认参数。
int age = 33;void test() {cout << "test()" << endl;
}void display(int a = 11, int b = 22, int c = age, void (*func)() = test) {cout << "a is " << a << endl;cout << "b is "<< b << endl;cout << "c is " << c << endl;func();
}int main() {display();
}
函数重载、默认参数可能会产生冲突、二义性(建议优先选择使用默认参数)
void display(int a, int b = 20) {cout << "a is " << a << endl;
}void display(int a) { cout << "a is " << a << endl;
}int main() {display(10); // 编译失败,存在二义性
}
extern "C"
被 extern "C"
修饰的代码会按照 C 语言的方式去编译
extern "C" void func() { cout << "func()" << endl;
}extern "C" void func1(int age) {cout << "func(int age) " << age << endl;
}
如果函数同时有声明和实现,要让函数声明被 extern “C” 修饰,函数实现可以不修饰
extern "C" void func();
extern "C" void func1(int age);void func() {cout << "func()" << endl;
}void func1(int age) {cout << "func(int age) " << age << endl;
}
extern "C" {void func();void func1(int age);
}void func() {cout << "func()" << endl;
}void func1(int age) {cout << "func(int age) " << age << endl;
}
由于C、C++ 编译规则的不同,在C、C++ 混合开发时,可能会经常出现以下操作:
-
C++ 在调用 C 语言 API 时,需要使用
extern "C"
修饰 C 语言的函数声明
-
有时也会在编写 C 语言代码中直接使用
extern "C"
,这样就可以直接被 C++ 调用了
#pragma once
我们经常使用#ifndef、#define、#endif
来防止头文件的内容被重复包含
#pragma once
可以防止整个文件的内容被重复包含
区别:
-
#ifndef、#define、#endif
受 C/C++ 标准的支持,不受编译器的任何限制 -
有些编译器不支持
#pragna once
(较老编译器不支持,如GCC 3.4版本之前),兼容性不够好 -
#ifndef、#define、#endif
可以针对一个文件中的部分代码,而#pragma once
只能针对整个文件
内联函数(inline function)
使用inline
修饰函数的声明或者实现,可以使其变成内联函数。建议声明和实现都增加inline
修饰。
特点:
- 编译器会将函数调用直接展开为函数体代码
- 可以减少函数调用的开销
- 会增大代码体积
注意:
-
尽量不要内联超过10行代码的函数
-
有些函数即使声明为
inline
,也不一定会被编译器内联,比如递归函数
VS中可通过如下配置选择禁用或启用内联扩展:
内联函数与宏
-
内联函数和宏,都可以减少函数调用的开销
-
对比宏,内联函数多了语法检测和函数特性
思考以下代码的区别
#define sum(x) (x + x)int a = 10;int main() {std::cout << sum(a++) << std::endl; // 21std::cout << a << std::endl; // 12
}
- 上面代码中
sum(a++)
等价于(a++) + (a++)
,sum
的计算结果会先取a
的值(10
)再取a++
之后a
的值(11
)所以之和是21
,而a
本身由于自增了两次所以结果是12
inline int sum(int x) { return x + x; }int a = 10;int main() {std::cout << sum(a++) << std::endl; // 20std::cout << a << std::endl; // 11
}
- 上面代码中
sum(a++)
属于函数调用,因此会捕获a
的副本(10
)传入函数中,在函数体中执行10 + 10
得到结果是20
,在这之后由于a
本身执行自增操作变成了11
表达式
C++ 的有些表达式是可以被赋值的
int a = 1;
int b = 2;
(a = b) = 3; // 赋值给了 a
(a < b ? a : b) = 4; // 赋值给了 b
const
-
const
是常量的意思,被其修饰的变量不可修改 -
如果修饰的是类、结构体(的指针),其成员也不可以更改
struct Student { int age; }; int main() {Student stu1 = {10};Student stu2 = {20};const Student *pStu1 = &stu1;*pStu1 = stu2; // 编译报错,*pStu1不能修改(*pStu1).age = 30; // 编译报错,*pStu1的成员不能修改pStu1->age = 30; // 编译报错,*pStu1的成员不能修改pStu1 = &stu2; // 编译成功,pStu1指向可以修改Student * const pStu2 = &stu2;*pStu2 = stu1; // 编译成功,*pStu2 可以修改(*pStu2).age = 30; // 编译成功,*pStu2 的成员可以修改pStu2->age = 30; // 编译成功,*pStu2 的成员可以修改pStu2 = &stu1; // 编译报错,pStu2不能修改指向
}
思考:以下5个指针分别是什么含义?
int age = 10;
const int *p0 = &age;
int const *p1 = &age;
int * const p2 = &age;
const int * const p3 = &age;
int const * const p4 = &age;
上面的指针问题可以用以下结论来解决:const
修饰的是其右边的内容
验证代码:
int main() {int a = 2;int age = 10;const int *p0 = &age;p0 = &a; // p0 可以修改*p0 = 3; // *p0 不能修改, 编译报错int const *p1 = &age;p1 = &a; // p1 可以修改*p1 = 3; // *p1 不能修改, 编译报错int * const p2 = &age;p2 = &a; // p2 不能修改, 编译报错*p2 = 3; // *p2 可以修改const int * const p3 = &age;p3 = &a; // p3 不能修改, 编译报错*p3 = 3; // *p3 不能修改, 编译报错int const * const p4 = &age;p4 = &a; // p4 不能修改, 编译报错*p4 = 3; // *p4 不能修改, 编译报错
}
引用(Reference)
- 在C语言中,使用指针(Pointer)可以间接获取、修改某个变量的值
- 在C++中,使用引用(Reference)可以起到跟指针类似的功能
int age = 20;
// rage 就是一个引用
int &rage = age;
注意点:
- 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
- 对引用做计算,就是对引用所指向的变量做计算
- 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
- 可以利用引用初始化另一个引用,相当于某个变量的多个别名
- 不存在【引用的引用、指向引用的指针、引用数组】
引用存在的价值之一:比指针更安全、函数返回值可以被赋值
引用的本质
-
引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针
-
一个引用占用一个指针的大小
常引用(Const Reference)
引用可以被const
修饰,这样就无法通过引用修改数据了,可以称为常引用
const
必须写在&
符号的左边,才能算是常引用
const
引用的特点:
- 可以指向临时数据(常量、表达式、函数返回值等)
- 可以指向不同类型的数据
- 作为函数参数时(此规则也适用于
const
指针):
✓ 可以接受const
和非const
实参(但非const
引用只能接受非const
实参)
✓ 可以跟非const
引用构成重载
注意:当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量。
数组的引用
常见的2种写法:
int array[] = { 10,20,30 };
int (&ref1)[3] = array;
int * const &ref2 = array; ref1[0] = 8;
int a = ref1[2];
ref2[1] = 4;
int b = ref2[2]
变量地址总结
一个变量的地址值,是它所有字节地址中的最小值
面向对象
类
- C++ 中可以使用
struct
、class
来定义一个类
struct
和 class
的区别:
struct
的默认成员权限是public
class
的默认成员权限是private
- 上面代码中
person
对象、p
指针的内存都是在函数的栈空间,自动分配和回收的 - 可以尝试反汇编
struct
和class
,看看是否有其他区别 - 实际开发中,用
class
表示类比较多
C++编程规范
每个人都可以有自己的编程规范,没有统一的标准,没有标准答案,没有最好的编程规范
变量名规范参考:
- 全局变量:
g_
- 成员变量:
m_
- 静态变量:
S_
- 常量:
C_
- 使用驼峰标识
对象的内存布局
思考:如果类中有多个成员变量,对象的内存又是如何布局的?
struct Person {int m_id; int m_age; int m_height;void display() {cout << "m id is " << m_id << endl;cout << "m_age is " << m_age << endl;cout << "m_height is " << m_height << endl;}
};
this
-
this
是指向当前对象的指针 -
对象在调用成员函数的时候,会自动传入当前对象的内存地址
struct Person {int m_id;int m_age;int m_height;void display() {cout << "m id is " << this->m_id << endl;cout << "m_age is " << this->m_age << endl;cout << "m_height is " << this->m_height << endl;}
};
思考:可以利用 this.m_age
来访问成员变量么?
- 不可以,因为
this
是指针,必须用this->m_age
指针访问对象成员的本质
思考:以下代码最后打印出来的每个成员变量值是多少?
struct Person {int m_id;int m_age;int m_height;void display() {cout << "m id is " << this->m_id << endl;cout << "m_age is " << this->m_age << endl;cout << "m_height is " << this->m_height << endl;}
};int main() {Person person;person.m_id = 10;person.m_age = 20;person.m_height = 30;Person *p = (Person *) &person.m_age;p->m_id = 40;p->m_age = 50;person.display();
}
答案输出是:
m id is 10
m_age is 40
m_height is 50
思考:如果将 person.display()
换成 p->display()
呢?
输出如下:
m id is 40
m_age is 50
m_height is -1063256544
此时 p->m_height
指向了一块未知的内存。、
封装
成员变量私有化,提供公共的getter
和setter
给外界去访问成员变量
struct Person{
private:int m_age;
public:void setAge(int age) {this->m_age = age;}int getAge() {return this->m_age;}
};Person person;
person.setAge(20);
cout << person.getAge() << endl;
内存空间的布局
每个应用都有自己独立的内存空间,其内存空间一般都有以下几大区域
-
代码段(代码区):用于存放代码
-
数据段(全局区):用于存放全局变量等
-
栈空间:
- 每调用一个函数就会给它分配一段连续的栈空间,等函数调用完毕后会自动回收这段栈空间
- 自动分配和回收
-
堆空间:需要主动去申请和释放
堆空间
在程序运行过程,为了能够自由控制内存的生命周期、大小,会经常使用堆空间的内存
堆空间的申请\释放
-
malloc \ free
-
new \ delete
-
new[] \ delete[]
注意:
-
申请堆空间成功后,会返回那一段内存空间的地址
-
申请和释放必须是1对1的关系,不然可能会存在内存泄露
现在的很多高级编程语言不需要开发人员去管理内存(比如Java),屏蔽了很多内存细节,利弊同时存在
-
利:提高开发效率,避免内存使用不当或泄露
-
弊:不利于开发人员了解本质,永远停留在API调用和表层语法糖,对性能优化无从下手
下图是x86环境(32bit):
堆空间的初始化
memset
memset
函数是将较大的数据结构(比如对象、数组等)内存清零的比较快的方法
Person person;
person.m_id = 1;
person.m_age = 20;
person.m_height = 180;
memset(&person, 0, sizeof(person));Person persons[] ={ { 1,20,180 }, { 2,25,165,},{ 3,27,170 } };
memset(persons, 0, sizeof(persons));
对象的内存
对象的内存可以存在于3种地方:
- 全局区(数据段):全局变量
- 栈空间:函数里面的局部变量
- 堆空间:动态申请内存(
malloc
、new
等)
构造函数(Constructor)
构造函数(也叫构造器),在对象创建的时候自动调用,一般用于完成对象的初始化工作
特点:
- 函数名与类同名,无返回值(
void
都不能写),可以有参数,可以重载,可以有多个构造函数 - 一旦自定义了构造函数,必须用其中一个自定义的构造函数来初始化对象
注意:
- 通过
malloc
分配的对象不会调用构造函数
一个广为流传的、很多教程/书籍都推崇的错误结论:
-
默认情况下,编译器会为每一个类生成空的无参的构造函数
-
正确理解:在某些特定的情况下,编译器才会为类生成空的无参的构造函数(哪些特定的情况?以后再提)
构造函数的调用
struct Person {int m_age;Person() {cout << "Person()" << endl;}Person(int age) {cout << "Person(int age)" << endl;}
}
默认情况下,成员变量的初始化
- 如果自定义了构造函数,除了全局区,其他内存空间的成员变量默认都不会被初始化,需要开发人员手动初始化。
成员变量的初始化
对象初始化
Person() {memset(this, 0, sizeof(Person));
}
析构函数(Destructor)
析构函数(也叫析构器),在对象销毁的时候自动调用,一般用于完成对象的清理工作
特点:
- 函数名以
~
开头,与类同名,无返回值(void
都不能写),无参,不可以重载,有且只有一个析构函数
注意:
- 通过
malloc
分配的对象free
的时候不会调用析构函数 - 构造函数、析构函数要声明为
public
,才能被外界正常使用。
声明和实现分离
命名空间
命名空间可以用来避免命名冲突
思考:如下代码能通过编译吗?
namespace MJ {int g_age;
}
namespace FX {int g_age;
}using namespace MJ;
using namespace FX;// 这句代码能编译通过么?
g_age = 20;
- 不能,必须指明命名空间前缀,否则具有二义性,编译器无法做出判断
命名空间的嵌套
有个默认的全局命名空间,我们创建的命名空间默认都嵌套在它里面
namespace MJ {namespace SS {int g_age;}
}int main() {MJ::SS::g_age = 10;using namespace MJ::SS;g_age = 20;using MJ::SS::g_age;g_age = 30;
}
int g_no = 20;namespace MJ {namespace SS {int g_age;}
}int main() {::g_no = 20;::MJ:SS::g_age = 30;
}
命名空间的合并
以下2种写法是等价的:
namespace MJ {int g_age;
}
namespace MJ {int g_no;
}
namespace MJ {int g_age;int g_no;
}
其他编程语言的命名空间
Java:
- Package
Objective-C:
- 类前缀
继承
继承,可以让子类拥有父类的所有成员(变量\函数)
struct Person {int m_age;void run() {cout << "Person::run()" << endl;}
};
struct Student : Person {int m_no;void study() {cout << "Student::study()" << endl;}
};int main() {Student student; student.m_age = 20; student.m_no = 1; student.run(); student.study();
}
关系描述
Student
是子类(subclass,派生类)Person
是父类(superclass,超类)
C++中没有像Java, Objective-C的基类:
- Java:
java.lang.Object
- Objective-C:
NSObject
继承关系中的对象内存布局
构造函数的初始化列表
- 一种便捷的初始化成员变量的方式
- 只能用在构造函数中
- 初始化顺序只跟成员变量的声明顺序有关
下面两种代码写法是等价的:
struct Person {int m_age;int m_height;Person(int age, int hei
相关文章:

【C++基础知识学习笔记】精华版(复习专用)
常用语法 函数重载(Overload) 规则: 函数名相同 参数个数不同、参数类型不同、参数顺序不同 注意: 返回值类型与函数重载无关 调用函数时,实参的隐式类型转换可能会产生二义性 默认参数 C++ 允许函数设置默认参数,在调用时可以根据情况省略实参。规则如下: 默认参数只能…...

探索ChatGPT在学术写作中的应用与心得
随着人工智能的迅猛发展,ChatGPT作为一种强大的自然语言处理模型,逐渐在学术界引起了广泛的关注。本文将探讨ChatGPT在学术写作中的应用,并分享使用ChatGPT进行学术写作时的一些经验和心得。 01 — ChatGPT在学术写作中的应用 1.文献综述和…...

Android:怎么学习才能更好的进大厂呢?
怎么学习才能更好的进大厂呢? 很多朋友都在问这个问题。 其实没有什么特别的技巧,就是依靠自己的毅力和决心。一天做不到,就一个月;一个月做不到,就一年。只要有决心,无论学历或资历如何,都不是…...

CSS标点符号换行问题
最近遇到一个奇怪的现象,元素中中文文本正常显示,但是加了一堆符号后中文文本居然换行了. div{width: 200px;border: 1px solid blue;word-break: break-all;} <div>文本</div>经过研究发现,因为标点符号不允许出现在行首和行尾,连带着符号…...

jdbc Preparestatement防止SQL注入的原理
2023-10-28T03:37:11.264132Z 2 Execute select * from users where username liulemon and password \ or \1\ 1\ 可以看到这一行,预编译时?变成了转义字符 useServerPrepStmtstrue加上这句才能预编译...

如何控制 LLM 的输出格式和解析其输出结果?
现在很多人对于如何使用像 ChatGPT 这样的 LLM 已经比较有经验了,可以使用各种不同的 Prompt 得到自己想要的结果。但有时候我们的使用场景不局限于手动操作,而是需要结合程序去调用 API,并且解析 API 的返回结果,从而实现一些自动…...

【Linux】 ps 命令使用
ps (英文全拼:process status)命令用于显示当前进程的状态,类似于 windows 的任务管理器。 语法 ps [选项] ps命令 -Linux手册页 著者 ps最初由布兰科兰克斯特撰写<lankestefwi.uva.nl>。迈克尔K约翰逊<johnsonmred…...

C++二分查找算法的应用:长度递增组的最大数目
本文涉及的基础知识点 二分查找 题目 给你一个下标从 0 开始、长度为 n 的数组 usageLimits 。 你的任务是使用从 0 到 n - 1 的数字创建若干组,并确保每个数字 i 在 所有组 中使用的次数总共不超过 usageLimits[i] 次。此外,还必须满足以下条件&…...

提示3D标题编辑器仍在运行怎么解决,以及3D标题编辑器怎么使用
在进行视频剪辑时,尤其是剪辑一些带有文字的开场视频,一般都会使用具有立体效果的3D标题,这样制作出来的视频效果不仅好看,还非常的炫酷,但是对于一些刚刚开始接触视频剪辑的小伙伴来说,可能对3D标题还不是…...

1. PPT高效初始化设置
1. PPT高效初始化设置 软件安装:Office 2019 主题和颜色 颜色可以在白天与黑夜切换,护眼 切换成了黑色 撤回次数 撤回次数太少,只有20次怎么办 自动保存 有时忘记保存就突然关闭,很需要一个自动保存功能 图片压缩 图…...

el-cascader级联选择器选中一个全选中问题
问题 只选中一个却把同级全选中 解决 :props中添加label、value、children属性 label、value、children属性值需要和后端返回的集合中的字段名保持一致 后端返回数据:...

Opencascad(C++)-创建自定义坐标系
文章目录 1、前言2、在Opencascad中显示小的坐标系3、在Opencascad中创建自定义的坐标系 1、前言 在Opencascad开发时,在view中可以显示小的坐标系,但是有时我们需要在建模时创建基准坐标系,当然可以作为工件坐标系也可以作为基准坐标系。本…...

MySQL数据库入门到大牛_01_数据库概述
文章目录 1. 为什么要使用数据库2. 数据库与数据库管理系统2.1 数据库的相关概念2.2 数据库与数据库管理系统的关系2.3 常见的数据库管理系统排名(DBMS)2.4 常见的数据库介绍 3. MySQL介绍3.1 概述3.2 MySQL发展史重大事件3.3 关于MySQL 8.03.4 Why choose MySQL?3.5 Oracle v…...

Web - Servlet详解
目录 前言 一 . Servlet简介 1.1 动态资源和静态资源 1.2 Servlet简介 二 . Servlet开发流程 2.1 目标 2.2 开发过程 三 . Servlet注解方式配置 编辑 四 . servlet生命周期 4.1 生命周期简介 4.2 生命周期测试 4.3 生命周期总结 五 . servlet继承结构 5.1 ser…...

postgresql 触发器如何生成递增序列号,从1开始,并且每天重置
大家好,我是三叔,许久不见,这期给大家介绍一下笔者在开发中遇到的业务处理:pgsql 创建触发器生成每日递增序列,并且第二天重置,根据不同的用户进行不同的控制。 1.创建生成递增序列的 table 表 -- 创建us…...

“第五十九天”
这是昨天那道题,这个后面自己的处理思路还是差了点,这道题关键感觉就是对进位的处理的,由于进位的存在,所以处理数据的时候只能从最低位开始,我一开始是从高位处理的,而且后面越来越迷,这个点一…...

IDEA集成Docker插件打包服务镜像与运行【附Docker命令汇总】
Docker官网 Docker官网:https://www.docker.com/ Docker Hub官网:http://hub.docker.com/ 什么是Docker Docker 是一个开源的容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者和系统管理员在笔记本上编…...
【Linux网络编程_TCP/UDP_字节序_套接字 实现: FTP 项目_局域网聊天项目 (已开源) 】.md updata:23/11/03
文章目录 TCP/UDP对比端口号作用字节序字节序转换api套接字 socket实现网络通讯服务端 逻辑思路demo: 满血版双方通讯/残血版多方通讯服务端 demo客户端 demo FTP 项目实现sever demo:client demo: 局域网多方通讯 配合线程实现sever demo:client demo: TCP/UDP对比…...

Leetcode刷题详解——全排列
1. 题目链接:46. 全排列 2. 题目描述: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],…...
JSONP 跨域访问(1), 简介, 原理, 实验, 缺点
JSONP 跨域访问(1), 简介, 原理, 实验, 缺点 一, JSONP 简介 JSONP(JSON with Padding)是一种非官方跨域数据交互协议。它允许web页面从不同的域名下加载数据。 由于同源策略,web页面通过XMLHttpRequest调用通常只允许访问与其自身相同域名…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...