day 5.c++语法
day 5.
引用的本质是指针实现的,所有类型的指针都是四字节;
右值引用也能改变内存实体
int main()
{
int a(4);
int && numr(move(a));//a是左值,移动语义把左值变右值
numr=1;
cout<<numr<<" "<<a<<endl;//都是1,右值引用和左值引用都能改变对象内容,右值引用更加强大,可以处理左值和右值;
return 0;
}
返回值是引用
int &test()
{
int num=10;
return num;
}
int main()
{
int &m=<test();
cout<<m<endl;//返回值是num,num在调用结束就已经销毁,引用指向的num那片地址重新被分配,所以是一个随机值,而不是10;
如果是
return 0;
}
只有栈区自动回收,返回值为指针、引用,不能返回指向栈区的变量,否则那片内存被回收就会被垃圾值覆盖
引用的本质是指针
二维数组
int main()
{
int a[5]{1,2,3,4,5};
int b[3][4]{1,2,3,4,5,6,7,8,9,10,11,12};
int *p(new int [5]{1,2,3,4,5});
int (*p2)[4]=new int[3][4]{1,2,3,4,5,6,7,8,9,10,11,12};//二维数组每个元素是一个一维数组,所以是一维数组的指针;
int *pa[2]{a,a+1};
int &ra[5]=a;
int * (&rpa)[2]=pa;
int *p1(new int[5]{1,2,3,4,5});
int **pp(new int*[5]{p1,p1+1...});//二级指针存储的是一级指针的地址,不能跨级;
return 0;
}
const与引用
int main()
{
int num=100;
const int &rnum=num;//引用用对象赋值不能直接 &rnum=100;这样找不到100的地址
//只能读不能写;
int a[5]{1,2,3,4,5};
cont int (&ra)[5]=a;
return 0;
}
函数指针
int gocmd(const char*cmd)
{
cout<<"cmd"<<endl;
return 0;
}
int main()
{
int (*p)(const char*cmd)(gocmd)//初始化函数指针;形参去不去掉都行,类型不能去
p(cmd);//调用
return 0;
}
多线程初步
#include<thread>
void run()
{
MessageBoxA(0"hello","ok",0);
}
void show(char,int)
{
MessageBoxA(0"hello","ok",0);
}
int main()
{
run();同步打开,就是一个打开结束才能打开另一个 ,阻塞
run();
thread t1(run);
thread t2(run);
thread t3(run); //并行弹出三个窗口,也就是同时弹出,无需等待;
//这个是类的初始化
thread t[3]{thread(run),thread(run),thread(run)} ;//类的构造函数进行调用
thread *p(new thread[3]{thread(run),thread(run),thread(run)}) ;堆上开辟线程;
多线程传递参数
thread th1(show,'a',2);//第一个是函数名,后面是函数形参
getchar();//这里要等待,不然main主线程退出,其他子线程也会秒退出;
return 0;
}
为了避免主线程挂了导致其他线程也挂,我们可以用join函数,让主线程等待其他线程完成再退出
int main()
{
thread th[3]{thread(show),thread(show),thread(show)};
for(int i=0;i<3;i++)
th[i].join();//让主线程等待这些join的线程结束再退出,这样我们就不用getchar()来阻塞主线程
return 0;
}
多线程最关键的是cpu是几核的,我们创建线程尽可能是核的倍数,这样就能负载均衡
detach
int main()
{
thread th(show);
th.detach()//脱离主线程,主线程挂了,它会自动退出,不报错;
//一旦detach后线程无法通信了,所以这学了好像科普没感觉哪有用;
th.joinable();//判断线程能否让主线程等待,一旦detach脱离就无法加入了;返回值是bool
return 0;
}
原子变量互斥线程安全
#include<thread>
#include<mutex>
#include<atomic> //原子变量
mutex m;//线程互斥量 一个类来的 加锁解锁能保证答案正确但是频繁加锁解锁浪费时间;
//int num=0;
atomic_int num{0}; 原子变量不用加锁上锁也能访问正确;
oid run()
{
for(int i=1;i<=10;i++)
{
num++;//当num是原子变量atomic_int 时,不用锁能保证正确,而且速度更快,不会发生线程冲突;
}
}
void run()
{
for(int i=1;i<=10;i++)
{
m.lock();//上锁 如果没有互斥量,多线程会同时访问变量i导致 num<=20;因为同时访问结束后只是+1;
num++;
m.unlock();//解锁
}
}
int main()
{
//多线程访问不冲突,安全;多线程访问冲突,不安全;
thread th1(run);
thread th2(run);
th1.join();
th2.join();
return 0;
}
lambda与多线程
#include<thread>
int main()
{
auto fun=[](){MessageBoxA(0,"1","2",0);}
thread th1(fun);
thread th2(fun);
thread th3([](){
cout<<this_thread::get_id();//获取当前线程ID;
});
return 0;
}
int main()
{
int *p=new int(5);
delete p;
//delete后p指向的内存被回收,p的指向是随机的,跟底层有关,指回那片内存或者其他地方,所以要把p设置nullptr避免再次delete引发其他严重Bug
p=nullptr; //避免野指针
//delete 和free对于基本数据类型都会释放,释放两次会出错,free不改变指针的值,delete会
return 0;
}
class mydata
{
public:
mydata()
{
cout<<"start"<<endl;
}
~mydata()
{
cout<<"delete"<<endl;
}
};
void test()
{
mydata d1;//创建调用构造函数,销毁调用析构函数
}
int main()
{
test();
mydata *p=new mydatda();//调用构造
delete p ;//调用析构
malloc不会调用构造函数;对于自定义类在cpp用new 和delete;否则造成内存泄漏
free(p)不会调用析构函数,别用free;
int *p=new int[3]{1,2,3};
delete []p;对于基本数据类型delete p和delete []p效果一样;
对于自定义类,单个数据类型不能用delete []p;否则会当成无限数组不停delete;
int *p=new mydata(10);//调用10次构造
delete []p;//这样就释放10次,如果是delete p;只会释放一个,造成内存泄漏,所以要配对
return 0;
}

浙公网安备 33010602011771号