C++新经典
- 第1章 C/C++
- 1.1 C和C++语言的起源、特点、关系与讲解范畴
- 1.2 C/C++语言市场需求与就业需求分析
- 1.3 再谈C/C++就业
- 1.4 搭建开发语言环境
- 第2章 数据类型、运算符与表达式
- 2.1 常量、变量、整形、实型和字符型
- 2.1.1 创建最基本的C程序
- 2.1.2 C语言的数据类型
- 2.1.3常量和变量
- 2.1.4 整型数据
- 2.1.5 实型数据
- 2.1.6 字符型数据
- 2.1.7 字符串常量
- 2.1.8 变量赋初值
- 2.1.9 数值型数据之间的混合运算
- 2.2 算数运算符和表达式
- 2.2.1 C语言的运算符
- 2.2.2 算术运算符和算数表达式
- 2.2.3 运算符优先级问题
- 2.2.4 强制类型转换运算符
- 2.25 自增和自减运算符
- 2.3 复制运算符和逗号运算符
-
- 第3章 程序的基本结构和语句
- 第4章 程序的基本结构和语句
- 第5章 循环控制
- 第6章 数组
- 第7章 函数
- 第8章 编译预处理
- 第9章 指针
- 第10章 结构体与公用体
- 第11章 位运算
- 第12章 文件
- 第13章 C++基本语言
- 13.6函数新特性、inline内联函数与const详解
- 13.6.1 函数回顾与后置返回类型
- 13.6.2 inline内联函数
- 13.6.3 函数特殊写法总结
- 13.6.4 关于const char * 、char const *、char * const
- 13.6.5 函数形参中带const和&
- 13.7 string 类型
- 13.8 vector类型
- 13.9 迭代器精彩演绎、失效分析及弥补、实战
- 13.10 类型转换:static_cast 、reinterpret_cast
- 13.10.1 隐式类型转换
- 13.10.2 显式类型转换(强制类型转换)
- 第14章 类
- 第15章 模板与泛型
- 第16章 智能指针
- 第17章 并发与多线程
- 17.1 基本概念和实现
- 17.1.1 并发、进程、线程的基本概念
- 17.1.2 并发的实现方法
- 17.1.3 C++11新标准线程库
- 17.2 线程启动、结束与创建线程的方法
- 17.2.1 范例演示线程运行的开始和结束
- 17.2.2 其他创建线程的写法
- 17.3 线程传参详解、detach坑与成员函数作为线程函数
- 17.3.1 传递临时参数对象作为参数
- 17.3.2 临时对象作为线程参数继续讲
- 第18章 内存高级话题
- 第19章 STL标准模板库大局观
- 第20章 高级话题与新标准
- 后记 IT职业发展的未来之路
第1章 C/C++
1.1 C和C++语言的起源、特点、关系与讲解范畴
1.2 C/C++语言市场需求与就业需求分析
1.3 再谈C/C++就业
1.4 搭建开发语言环境
第2章 数据类型、运算符与表达式
2.1 常量、变量、整形、实型和字符型
2.1.1 创建最基本的C程序
2.1.2 C语言的数据类型
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210326151649684.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTQ1NDg1Mg==,size_16,color_FFFFFF,t_70#pic_center)
1.每种数据类型所占内存大小
2.每种数据的取值范围
2.1.3常量和变量
常量:字面值。
变量:值可以改变的量。
变量定义:类型名 变量名 [ = 变量初始值 ];
标识符:变量等等的取名。
保留字:系统保留的特殊用途的字。
2.1.4 整型数据
整数,包含所有进制。
2.1.5 实型数据
小数。
2.1.6 字符型数据
1.字符常量
‘a’ , ‘b’ ,‘1’
2.转义字符
‘\n’ ‘\’ ‘’’ …
3.字符变量
char c = ‘a’ ;
printf("c1=%c, c2=%c\n",c1,c2);
printf("c1=%d, c2=%d\n",c1,c2);
printf("%f",num);
知识点: ASCII
2.1.7 字符串常量
printf("hello world!");
注:Ctrl+F5 调试
Ctrl+F9 断点
Ctrl+F10下一断点
Shift +F9 添加监视
Ctrl Alt M +1 调用内存1(调试-窗口-内存-内存1)
2.1.8 变量赋初值
定义变量之后才能使用,一般是要赋初值的,否则会出错。
2.1.9 数值型数据之间的混合运算
不同数值类型的数据一起运算的的时候,系统尝试将变量类型统一。
类型转换从:
char , short ---->int ----> unsigned ----> long ----> double <-----double <----float
范围:由小变大。(隐式转换)
2.2 算数运算符和表达式
2.2.1 C语言的运算符
算数运算符 + - * 、 %(取余)
关系运算 <, <=, ==,>, >=,!=
逻辑运算符 !, &&, ||
位运算符 <<, >>, ~, |, ^, &
赋值运算符 =
条件运算符 ? :
逗号运算符 ,
指针运算符 * , &
求占字节运算符 sizeof()
强制类型转换符 (类型名)
成员变量运算符 . , ->
下标运算符 []
其他 …
2.2.2 算术运算符和算数表达式
2.2.3 运算符优先级问题
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210326164040477.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTQ1NDg1Mg==,size_16,color_FFFFFF,t_70#pic_center)
![P29](https://img-blog.csdnimg.cn/20210326164115905.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTQ1NDg1Mg==,size_16,color_FFFFFF,t_70#pic_center)
2.2.4 强制类型转换运算符
一般形式: (类型名)(表达式)
int a=1;
double b=(int) a;
总结:两种数据类型转换
都有精度损失或者溢出的风险。
2.25 自增和自减运算符
前++
后++
只适用于整型变量。
2.3 复制运算符和逗号运算符
2.3.1 赋值运算符和赋值表达式
第3章 程序的基本结构和语句
第4章 程序的基本结构和语句
第5章 循环控制
第6章 数组
第7章 函数
第8章 编译预处理
第9章 指针
第10章 结构体与公用体
第11章 位运算
第12章 文件
第13章 C++基本语言
13.6函数新特性、inline内联函数与const详解
13.6.1 函数回顾与后置返回类型
前置返回类型
int func( int a , int b);
int func( int a , int b)
{
return a+b;
}
后置返回类型
auto func( int a, int b)->int ;
auto func( int a, int b)->int
{
return a+b;
}
提示:一般一个函数写个几十到几百行就行,别太多,难以阅读。
13.6.2 inline内联函数
将简短且常用的函数定义为内联函数,代码在编译时被编译器会自动替换函数为函数体。
- 循环、分支、递归调用就不要使用内联修饰符。
- 内联函数类似 宏展开 #define 和 constexpr
inline int myfunc()
{
return 1;
}
13.6.3 函数特殊写法总结
void func(int a, int b){
a+b;
}
int func(int a, int b){
return a+b;
-返回指针
int * func(int b)
{
int a=b;
return &a;
}
int & func()
{
int a=0;
return a;
}
- 采用形参返回
void func(int &a ,int &b)
{
int temp=a;
a=b;
b=temp;
}
13.6.4 关于const char * 、char const *、char * const
关于修饰符:修饰符是从变量的旁边从右向左阅读
因此:
- const char * p ,* 先声明 p是指针,char 声明 指针指向的数据类型, const声明 不能通过指针p去修改p指的内容。
- char const * p,* 先声明 p是指针,const 声明不能通过指针p修改p指向的内容,声明 指针指向的数据类型。
- char * const p,const p p是一个常量, *声明这个常量是一个指向常量的指针,char常量指针指向的数据类型。
太难解释了,总之就是
- 指针常量,不能用指针求修改,指向的可以是常量,也可以是变量。(1,2)
-常量指针, 指针指向的是一个常量,不能指向非常量。(3)
13.6.5 函数形参中带const和&
先导:
形参的三种传值方式:
- 值传递
int add(int a, int b)
{
cout<<"a的地址:"<<&a<<std::endl;
cout<<"b的地址:"<<&b<<std::endl;
}
int main()
{
int c=1,d=2;
cout<<"c的地址:"<<&c<<std::endl;
cout<<"d的地址:"<<&d<<std::endl;
add(c,d);
}
- 指针传递
int add(int *a, int *b)
{
cout<<"a指向的地址:"<<a<<std::endl;
cout<<"a的地址:"<<&a<<std::endl;
cout<<"b指向的地址:"<<b<<std::endl;
cout<<"b的地址:"<<&b<<std::endl;
}
int main()
{
int e=1,f=2;
int *c=&e,*d=&f;
cout<<"c的地址:"<<&c<<std::endl;
cout<<"d的地址:"<<&d<<std::endl;
add(c,d);
}
- 引用传递
int add(int &a, int &b)
{
cout<<"a的地址:"<<&a<<std::endl;
cout<<"b的地址:"<<&b<<std::endl;
}
int main()
{
int c=1,d=2;
cout<<"c的地址:"<<&c<<std::endl;
cout<<"d的地址:"<<&d<<std::endl;
add(c,d);
}
综上,值传递需要消耗时间去复制实参到形参。
指针传递和引用传递是直接调用实参,效率更高。
函数参数带const
- 防止无意中修改形参值导致实参被修改
- 实参类型可以更加灵活
void func(const int & a)
{
a=1;
}
13.7 string 类型
13.8 vector类型
13.9 迭代器精彩演绎、失效分析及弥补、实战
迭代器仅仅是用来遍历容器的,若在遍历容器时,对容器进行修改,例如删除、添加的操作,会导致迭代器失效。
禁止迭代器使用时修改容器容量!
13.10 类型转换:static_cast 、reinterpret_cast
13.10.1 隐式类型转换
13.10.2 显式类型转换(强制类型转换)
C语言风格:
double b=10.1;
int a =(int) b;
C++四种强制类型转换
通用形式:
- static_cast :静态转换,即编译时检查。
double f=10.2;
int a= static_cast<int> (f);
int b=(int ) f;
class A{};
class B :public A{};
B b;
A a= static_cast<A> (b);
- void * 与其他类型指针之间的转换
void * 的指针可以指向任何指针类型。我感觉这个功能很鸡肋,懒得记载了。
不过,指针类型不可以进行强制类型转换,否则会出错。
-
dynamic_cast :运行时类型识别和检查
-
const_cast :用于去除指针或者引用的const属性,编译时检查。
在这里插入代码片
- reinterpret_cast
第14章 类
第15章 模板与泛型
第16章 智能指针
第17章 并发与多线程
17.1 基本概念和实现
17.1.1 并发、进程、线程的基本概念
1.并发
多个任务同时进行。(边听音乐边写作业,听音乐和写作是两不同的活动,但是同事进行。)
早期计算机只有一个CPU,为了同时间执行多个任务,我们规定在一定的时间间隔内执行程序,这样就可以在宏观上达到并行执行程序,但实际微观上是隔很短的时间不停的切换执行程序,这样看起来就像很多程序同时执行一样。
2.可执行程序
盘上的一个文件,window上是 .exe 结尾的文佳 ,Linux上面是 文件权限为 -rwxrw-r–的文件。
3.进程
可执行程序运行起来就是一个进程,总之就是一个运行的程序的过程就是进程。
4.线程
请先记住两件事:
- 每个进程都有一个主线程,这个主线程是唯一的。
- 当运行一个可执行程序,产生一个进程后,这个主线程也随之默默启动起来了。
总结:
- 线程是用来执行代码的
- 把线程理解成一条代码执行的通路,一个新的线程就代表一条新的道路。
- 一个进程自动办好了一个主线程,主线程随着进程的默默启动并运行,并可以包括多个其他线程(非主线程,是需要用代码来创建其他线程),但创建线程的数量最大一般不建议超过200~300个,具体还要针对实际程序。
- 因为主线程是自动启动的,所以一个进程中最少也是有一个线程的。
- 多线程可以同时做多件事情,所以运行效率更高,但是并不容易评估和量化,仍旧需要在实际编程和实际项目中体会和调整。
5.学习心得
戒骄戒躁,慢慢来。
17.1.2 并发的实现方法
1.多进程并发
多个程序同时运行。进程通讯三种方式:管道通讯、消息通讯和共享内存。
2.多线程并发
单个进程创建多个线程。线程通信代价更小,同一个进程的线程是共享地址空间,因此多线程并发开销更小。但是由于共享地址空间,数据一致性问题凸显。
3.总结
多线程并发的优缺点:
优点:线程启动速度更快,更轻量级,系统资源开销更少,执行速度更快。
缺点:使用起来有难度,要小心处理数据一致性问题。
17.1.3 C++11新标准线程库
C++11的线程库可以跨平台,使用起来更加方便。
17.2 线程启动、结束与创建线程的方法
17.2.1 范例演示线程运行的开始和结束
#include <iostream>
#include <thread>
using std::cout;
using std::endl;
void myprint()
{
cout << "我的线程开始执行了!" << endl;
cout << "我的线程执行结束了。" << endl;
return;
}
int main()
{
std::thread mytobj(myprint);
mytobj.join();
cout << "main主函数执行结束了!" << endl;
return 1;
}
(1)thread
一个类。构造函数的参数是一个可调用对象(此处可调用对象就是函数myprint)作为thread构造函数的实参来构造这个thread对象。
(2)join
阻塞函数,让main主线程等待子线程执行。(主线程结束,子线程会被强制结束)
(3)detach
分离函数,让main主线程不等待子线程执行。
(4)joinable
bool res=mytobj.joinable();//true为调用过。
判断某个线程是否调用过join或者detach函数。
17.2.2 其他创建线程的写法
1.用类来创建线程
#include <iostream>
#include <thread>
using std::cout;
using std::endl;
class TA {
public:
void operator ()() {
cout << "TA::operator()开始执行了" << endl;
cout << "TA::operator()执行结束了" << endl;
}
};
int main()
{
TA ta;
std::thread myTObj(ta);
myTObj.join();
cout << "main主函数执行结束了!" << endl;
return 1;
}
一些危险的操作:
#include <iostream>
#include <thread>
using std::cout;
using std::endl;
class TA {
public:
int& m_i;
TA(int& i):m_i(i){}
void operator ()() {
cout << "mi1的值为:" << m_i << endl;
cout << "mi2的值为:" << m_i << endl;
cout << "mi3的值为:" << m_i << endl;
cout << "mi4的值为:" << m_i << endl;
cout << "mi5的值为:" << m_i << endl;
cout << "mi6的值为:" << m_i << endl;
cout << "mi7的值为:" << m_i << endl;
cout << "mi8的值为:" << m_i << endl;
cout << "mi9的值为:" << m_i << endl;
}
};
int main()
{
int my_i = 6;
TA ta(my_i);
std::thread myTObj(ta);
myTObj.detach();
for (int i = 0; i < 1000000; ++i) {
int q;
q = 1;
}
cout << "main主函数执行结束了!" << endl;
return 1;
}
2.用lambda表达式来创建线程
#include <iostream>
#include <thread>
using std::cout;
using std::endl;
auto myLamThread = [] {
cout << "我的线程开始执行了" << endl;
cout << "我的线程执行结束了" << endl;
};
int main()
{
std::thread myTObj(myLamThread);
myTObj.join();
cout << "main主函数执行结束了!" << endl;
return 1;
}
17.3 线程传参详解、detach坑与成员函数作为线程函数
17.3.1 传递临时参数对象作为参数
1.要避免的陷阱1
主线程已经运行结束,资源已经释放,但是子线程仍然在调用主线程资源。
#include <iostream>
#include <thread>
using std::cout;
using std::endl;
using std::thread;
void myPrint(const int& i, char* pmyBuf)
{
cout << i << endl;
cout << pmyBuf << endl;
return;
}
int main()
{
cout << "main主线程开始执行" << endl;
int mvar = 1;
int& mvary = mvar;
char myBuf[] = "this is a test!";
thread myTObj(myPrint, mvar,myBuf);
myTObj.detach();
cout << "main主函数执行结束了!" << endl;
return 1;
}
2.要避免的陷阱2
传递的参数隐式转换时,还未来的及转换转换,资源就被释放。
#include <iostream>
#include <thread>
#include <string>
using std::cout;
using std::endl;
using std::thread;
using std::string;
void myPrint(const int& i, const &string pmyBuf)
{
cout << i << endl;
cout << pmyBuf << endl;
return;
}
int main()
{
cout << "main主线程开始执行" << endl;
int mvar = 1;
int& mvary = mvar;
char myBuf[] = "this is a test!";
thread myTObj(myPrint, mvar,myBuf);
myTObj.detach();
cout << "main主函数执行结束了!" << endl;
return 1;
}
总结:
- 若传递int这种简单类型参数,建议都使用值传递,不用用引用类型,避免节外生枝。
- 若传递类对象作为参数,则避免隐式转换(例如把一个char * 转换成string,一个int转成类A对象),全部都在创建线程这一行就构造出临时对象来,然后仙城入口函数的形参位置使用引用来作为形参(如果不使用引用可能在某种情况下导致多构造一次临时对象,不单浪费,且会造成新的潜在问题)。
- 总计结论:使用join就没有这个问题了。
17.3.2 临时对象作为线程参数继续讲
1.线程id概念
每个线程对应这不同的id。
使用:std::this _thread::get_id()、
2.临时对象构造时机抓捕
第18章 内存高级话题
第19章 STL标准模板库大局观
第20章 高级话题与新标准
后记 IT职业发展的未来之路
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)