day4. c++语法

day4
可变参数模板
#include<cstdarg>
tempate<typename T,typename...Args> //

#include<bits/stdc++.h>
using namespace std;

void show(){//递归结束需要的程序,相当于重载
}
template<class T,class...Args>//可变参数模板
void show(T t,Args...args)// Args...是泛类型,arg是参数
{
cout<<t<<endl;
show(args...);//args...是泛参数;
}
int main()
{

show(1,2.3,"123",'a');
return 0;
}

默认参数赋值//c++有,c没有
int add(int a=1,b=2,c=3)默认的参数要放右边,不能交错 即 int a=1, int b,int c=2; 错误
{
return a+b+c;//如果实参不够或者没有会自动匹配默认; 外观模式
}
c 的auto 是局部变量,c++的auto 是推理类型,有很大的不同


#include<bits/stdc++.h>
using namespace std;

template<class T1,class T2>//auto配合模板的泛型太牛了;
auto add(T1 t1,T2 t2)//这里用auto 就可以自动推理类型,不然返回值是T1或者T2都会造成结果不精确如add(10,10.1) add(10.1,10);
{
return t1+t2;
}
int main()
{
cout<<add(10,10.2)<<endl;
cout<<add(10.2,10)<<endl;
return 0;
}

decltype再谈
c++的初始化 用()初始化,其他情况不能, 如 int a(5)//初始化成5 int b;b(10)这样是不行的
decltype就是用按照常理b想用a的类型当作自己的类型;
decltype(a) b;//decltype(a)相当于推理a的类型;
数组初始化 int a[5]{0}//;花括号初始化

int a=5;
int *p=&a;
decltype(*p) b=5//这里一定要赋值,因为decltype (*p)是一个解引用过程,解出来是int & 引用必须要有初始值;

cpp数组初始化
int a[5]{1,2,3,4,5};//c风格int a[5]={1,2,3,4,5};
堆上可以
int* p=new int[5]{1,2,3,4,5};
进化版 int *p(new int[5]{1,2,3,4,5}) //cpp写法
cpp风格数组 arry<int,10>myint{0};//<类型,大小>
cpp的 空指针是nullptr 类型是指针类型, C是NULL默认是0,int类型;所以cpp用nullptr;
typedef 的用法是 将类型变量名字换掉
如typedef int a;把a当成int 类型别名
typedef int a[5];把a当成 int [5]的类型 a b等价于 int b[5];
typedef是 c风格的别名替换,Cpp独有的函数模板处理不了,using 是cpp风格


typedef double db;//db等价于double
using db=double;//db等价于double

typedef int a[10];//a等价于int [10]类型数组类型
using a=int [10];

typedef int (*p)(int a,int b);//函数指针别名 p;
using p=int (*)(int a,int b);

typedef int (*p[10])(int a,int b);//函数指针数组 p;返回值是一个函数指针数组;
using p=int (*[10])(int a,int b);右边定义类型变量,然后把变量名去掉就是别名

auto推理
c++11要指定返回类型, c++14不用 ,编译环时添加 -std=c++14;

auto add(int a,int b)->int//c++11
{
return a+b;
}

auto add(int a,int b)//c++14
{
return a+b;
}

收缩转换用花括号能保证类型转换

char a(1222222);这样编译器不会报错,但是后面的数据超出char范围了,会得到一个垃圾数据;
安全做法 char a{22222};这样编译器会报错,收缩转换;


constexpr标准返回值或者其他表达式是常量;

int get()
{
return 5;
}
int a[5+get()];//报错因为不是常量表达;

修改
constexpr int get()
{
return 5;
}

一个函数内部不能包含其他函数;但是用lambda可以解决这个问题

lambda返回值是一个函数指针;
如[](){cout<<"hello";} ()这样能调用函数 等价于auto fun= [](){cout<<"hello";}; fun();

auto fun=[](int a,int b)->int{cout<<"hello";return a+b;} 函数返回值是int 类型
lambda函数是内联展开无法取地址
[=](){ } //[=]是只读不能写, [&]可以写
[&](){ }//可读可写
[=]()mutable{ num=10;num2=20;} //这样num变成副本 ,可写可读;

指定某些可读某些可写

int main()
{
int a=10,b=9,c=8;
[&a,b,c](){a=20;cout<<b<<c<<endl;}//a可读可写,bc只能读。混合机制

return 0;
}

函数包装器function

void go()
{
cout<<"go";
}
int add(int a,int b)
{
return a+b;
}
int main()
{

function<void(void)>fun=go; //void是返回值(void)是参数为空
fun();
也可以和lambda配合
function<void(void)>fun2=[](){cout<<"ooo"<<endl;}
fun2();
function<int(int,int)>fun3=add;
fun3(1,2);
return 0;
}


模板元能解决递归加速问题!原理是把运行时间放在编译时间处理;
template<int N>
struct data
{
enum{res=data<N-1>::res+data<N-2>::res};
;}
template<>
struct data<1>
{
enum{res=1};
};
template<>
struct data<2>
{
enum{res=2};
};
int main()
{
cout<<data<40>::res;

return 0;
}

const 再谈

c语言的const 不是意义上的常量,不能直接修改,能间接修改
int main()
{
const int num=100;
int a[num];//c不可以,cpp可以
*(int*)&num=4;
cout<<num;//4;

return 0;
}
c++的const

int main()
{
const int n=10;
int a[n];//可以

return 0;
}
const 数组
int main()
{


const int num[2]{1,2};
const int *p=num;
*(int*)p=66;
cout<<num[0];//66可以间接修改不能直接修改

return 0;
}

智能指针;//用于解决内存泄漏,避免忘记delete而导致的内存泄露,智能指针托管会自动帮我们delete;
int main()
{

//double *p=new double[1024*1024*10]//80M;1024*1024*8*10;
//delete p;//清空p指向的内存
unique_ptr<double>p(new double[1024*1024*10]);//初始化
return 0;
}


tuple//元组 多元数组
数组里元素类型可以不一样;
#include<iostream>
#include <tuple>
using namespace std;
int main()
{
char a='a';
int b=2;
double c=3.0;
char* p="afdasd";//c++用char*指向字符串常量会警告,因为不安全,字符串是常量;
const char* p2="45sf";//char*和const char *都能指向字符串,只是const char*不能修改字符串
tuple<char,int,double>mytuple{a,b,c};//但是不能用for(auto &p:mytuple)这种访问
但是可以用vector<tuple<int,pii>>f;//vector套tuple比pair好用 访问比较麻烦暂时不懂
for(int i=0;i<f.size();i++)
{
auto p1=get<0>(f[i]);//获取第一个类型
auto p2=get<1>(f[i]);//第二个类型
auto p3=get<2>(f[i]);//3;
}
return 0;
}


左值与右值引用
左值与右值这两概念是从 c 中传承而来的,在 c 中,左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式).
左值
int main()
{
int num=10;//左值有内存实体可以赋值
int &rnum(num);//typedef using 类型别名;引用是变量别名
cpp能用引用 就引用别指针;
return 0;
}
右值
int main()
{
int num=1;
int &&rnum(num+4);//num+4在寄存器里面;右值引用 右值在寄存器不能取地址如Num+4:但是rnum是变量可以取地址,要区分开
右值引用可以节约效率;
因为左值引用是int data=num+4;int &lnum(data);
return 0;
}

int main()
{
int a[5]{1,2,3,4,5};
int * &pl=a;//左值
int * &&pr=a+1;右//值引用
}

移动语义

void show(int &&rnum)右值引用
{
cout<<rnum<<endl;
}
int main()
{
int a[5]{1,2,3,4,5};
show(a[3]+2);//6
show(a[3]);//a[3]是左值 报凑
解决方案是 show(move(a[3]));//move把左值转变成右值
return 0;
}

上一篇:Navicat15 试用


下一篇:【chrome插件】Auto Refresh Plus 自动刷新页面