C++与C的区别
1. C++风格数组初始化:
#include <iostream> #include <array> using namespace std; void main() { //CPP一维数组初始化 array<int, 10>MyInt{ 1,2,3,4,5,6,7,8,9,10 }; for (auto i : MyInt) { cout << i << " " << (void *)&i << endl; } //CPP二维数组初始化 array<int, 10>MyInt1{ 11,2,3,4,5,6,7,8,9,10 }; array<int, 10>MyInt2{ 21,2,3,4,5,6,7,8,9,10 }; array<int, 10>MyInt3{ 31,2,3,4,5,6,7,8,9,10 }; array<array<int, 10>, 3>myInt{ MyInt1,MyInt2,MyInt3 }; for (auto j : myInt) { for (auto i : j) { cout << i << " "; } cout << endl; } cin.get(); }
2. CPP别名:
#include <iostream> using namespace std; //一般情况,原则上都用using取代typedef //typedef处理不了模板,处理不了命名空间,统一用using typedef double DB; //C风格 using DBCPP = double; //CPP风格别名 //数组别名 typedef int a[10]; using IntArray = int[10]; //函数指针 typedef int(*p)(int a, int b); //函数指针类型,p using pFun = int(*)(int a, int b); //CPP函数指针别名 //函数指针数组 typedef int(*pa[10])(int a, int b); using ppFun = int(*[10])(int a, int b); void main() { cout << sizeof(DB) << endl; //8 cout << sizeof(DBCPP) << endl; //8 IntArray int1; cout << sizeof(int1) << endl; //40 cin.get(); }
3. auto 使用:
#include <iostream> using namespace std; //C++14自动推理返回值类型 //C++11需要指定返回值类型 auto add(int a, int b)->int //此处int可换为decltype(a+b) { return a + b; } //数控科技面试题:请问f是什么? auto (*f)()->int(*)(); //int(* (*fx)() )() //返回值是函数指针 //请推理下面pf是什么类型? auto pf1()->auto(*)()->int(*)() { return nullptr; //返回空指针 } auto pf2(void)->auto(*)(int x)->int(*)(int a,int b) { return nullptr; //返回空指针 } //int(* (*)(int x) )(int a,int b) //int(* (* pf2(void) )(int x) )(int a,int b) //int(*(* (void) )(int))(int,int) auto go(int a, int b)->auto(*)()->int (*)(int, int(*)(int, int)); //int (* (*)() )(int, int(*)(int, int)) //int (* (* go(int a, int b) )() )(int, int(*)(int, int)) //int (* (* (int, int))(void))(int, int (*)(int,int)) void main() { cout << typeid(f).name() << endl; cout << typeid(pf1).name() << endl; cout << typeid(pf2).name() << endl; cout << typeid(go).name() << endl; cin.get(); }
4. 函数模板的别名:
#include <iostream> #include <array> using namespace std; //C++14自动推理返回值类型 //C++11需要指定返回值类型 template<class T1,class T2> auto add(T1 t1, T2 t2)->decltype(t1 + t2) //此处int可换为decltype(a+b) { return t1 + t2; } //函数模板的别名 template<class T> using t = T; template<class T> using tp = T*; template<class T> void show(T tx) { t<int> t1(tx); //一旦使用别名必须指定类型 <int> tp<int> tp1(&tx); //初始化指针 cout << t1 << " " << tp1 << endl; } template<class T> using ten = array<T, 10>; void main() { cout << add(1, 2) << endl; //3 cout << add(1.1, 2.2) << endl; //3.3 int a = 10; show(a); using IntArray = array<int, 10>; //相当于为模板起了别名,明确地指明了类型为array<int, 10> array<int, 10> MyInt; //此时array<int, 10>代表一个类型 IntArray MyInt2; //template<class T> using t = array<T, 10>; //error C2951: 模板 声明只能在全局、命名空间或类范围内使用 ten<int> t1{ 1,2,3,4,5,6,7,8,9,10 };//模板别名不明确类型 for (auto i : t1) { cout << i << " "; } cin.get(); }
5. 收缩转换:
#include <iostream> using namespace std; void main() { char ch1(787878); char ch2{ 787878 }; //赋初始值最好用大括号,保证数据类型安全转换 cout << (int)ch1 << endl; //-90 编译通过,产生溢出 cout << (int)ch2 << endl; //编译不通过,error C2397: 从“int”转换到“char”需要收缩转换 cin.get(); }
6. 二进制:
#include <iostream> using namespace std; void main() { int a = 0b1001; int b = 0B1101; cout << a << endl; //9 cout << b << endl; //13 cin.get(); }
7. 常量表达式constexpr :
#include <iostream> using namespace std; int get1() { return 5; } constexpr int get2() //constexpr返回值或其他表达式为常量 { return 5; } void main() { //int a[5+get1()]; //error C2131: 表达式的计算结果不是常数 int a[5 + get2()]; //编译成功 cin.get(); }
8. lambda 表达式:
#include <iostream> using namespace std; void main() { [] {cout << "hello world!" << endl; }(); //解决函数怀孕现象 auto fun = [] {cout << "hello" << endl; }; //函数指针 fun();//调用 [] {cout << "hello C" << endl; }(); //匿名lambda表达式 [](char *str) {cout << str << endl; }("你好!");//带参数(匿名) auto fun1 = [](double a, double b) {return a + b; }; cout << fun1(10, 20.1) << endl; //30.1 auto fun2 = [](double a, double b)->int {return a + b; }; //指定返回值类型,->在()与{}之间 cout << fun2(10, 20.1) << endl; //30 int num = 100; auto func = [](int num) {num = 5, cout << num << endl; };//遵循副本机制 func(num); //5 cout << "main=" << num << endl; //main=100 cin.get(); }
#include <iostream> #include <array> #include <algorithm> using namespace std; void main() { int num1 = 100; int num2 = 99; //[]() {cout << num1 << " " << num2 << endl; }();//error C3493: 无法隐式捕获“num1”,因为尚未指定默认捕获模式 [=]() {cout << num1 << " " << num2 << endl; }(); //100 99 //[=]() {num1=10,num2=20,cout << num1 << " " << num2 << endl; }();//error C3491: “num1”: 无法在非可变 lambda 中修改通过复制捕获 [&]() {num1 = 10, num2 = 20, cout << num1 << " " << num2 << endl; }();//=只能读不可写;&读写外部变量 cout << "main=" << num1 << " " << num2 << endl; //main=10 20 int num3 = 100; int num4 = 99; [=]()mutable {num3 = 10, num4 = 20, cout << num3 << " " << num4 << endl; }(); //10 20 mutable起到副本作用 cout << "main=" << num3 << " " << num4 << endl; //100 99 int a = 10, b = 9, c = 8; //a可读可写,bc只可读 [&a, b, c]() {a = 20, cout << a << " " << b << " " << c << endl; }(); //20 9 8 //mutable副本,能读能写,读的原本,写的副本 [a, b, c]()mutable {a = 1, b = 2, c = 3, cout << a << " " << b << " " << c << endl; }(); //1 2 3 array<int, 10>MyInt{ 1,2,3,4,5,6,7,8,9,10 }; //lambda表达式嵌入使用 for_each(MyInt.begin(), MyInt.end(), [](int num) {cout << num << " "; }); //显示 cout << endl; for_each(MyInt.begin(), MyInt.end(), [](int &num) {num += 1, cout << num << " "; }); //修改 cout << endl; for_each(MyInt.begin(), MyInt.end(), [](int num) {cout << num << " "; }); //显示 cin.get(); }
9. 函数包装器:
#include <iostream> #include <functional> //函数包装器头文件 using namespace std; using std::function; //函数包装器 void go() { cout << "go" << endl; } int ADD(int a, int b) { return a + b; } void main() { function<void(void)> fun1 = go; //包装函数 fun1(); function<void(void)> fun2 = []() {cout << "go lambda" << endl; };//包装lambda表达式 fun2(); function<int(int, int)> fun3 = ADD; cout << fun3(10, 19) << endl; function<int(int, int)> fun4 = [](int a, int b)->int {return a + b; }; cout << fun4(10, 19) << endl; cin.get(); }
10. 模板元
#include <iostream> using namespace std; int get50(int n) { if (n == 1) return 1; else if (n == 2) return 2; else return get50(n - 1) + get50(n - 2); } //递归调用:函数反复调用,等待返回,浪费时间较多 //模板元实现递归加速 //执行速度快,编译的时候慢,代码会增加 //把运行的时间节约在编译的时候 //常使用在递归加速,游戏优化,此处的模板元仅仅适用于C++11 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 }; }; void main() { cout << data<40>::res << endl; //模板元,代码加速(将运行时间放在编译中执行) cout << get50(40) << endl; cin.get(); }
11. C++中的const :
#include <iostream> using namespace std; void run1(const int *p); //可改变地址,不可改内容 void run2(int const *p); // void run3(int * const p); //不可改地址,可改内容 void run4(const int * const p); //地址内容均不能改 void run5(int const * const p); // void main() { //C语言中: //const int num = 10; //*(int *)(&num) = 4; //printf("%d\n", num); //4 //const int n = 10; //int a[n]; //C++编译器自动优化,将n直接替换为10 //int a = 10; //const int n = a; //此时C++编译器不敢乱优化,不敢把n直接替换为a //int data[n]; //error C2057: 应输入常量表达式 //const int a = 10; //const int n = a; //int data[n]; //const int num = 10; //*(int *)(&num) = 3; //cout << (void *)&num << endl; //优化,强行替换为10,寄存器 //cout << *(&num) << endl; //10 //cout << num << endl; //10 //int a = 10; //const int num = a; //*(int *)(&num) = 3; //cout << (void *)&num << endl; //直接读内存,此时为变量,不敢优化 //cout << *(&num) << endl; //3 //cout << num << endl; //3 const int num[5] = { 1,2,3,4,5 }; const int *p = num; *(int *)p = 100; //const数组没有优化,可以间接改变 for (auto i : num) cout << i << " "; //100 2 3 4 5 cout << endl; cin.get(); }
12. 智能指针:
#include <iostream> #include <memory> #include <Windows.h> using namespace std; void cmem() { while (1) { double *p = new double[1024 * 1024 * 10]; Sleep(3000); delete p; //释放 Sleep(3000); } } void autoptr() { while (1) { double *p(new double[1024 * 1024 * 10]); auto_ptr<double>autop(p); //创建智能指针,接管这片内存,会自动回收 Sleep(3000); } } void autoptrnew() { while (1) { Sleep(6000); unique_ptr<double>p(new double[1024 * 1024 * 10]); Sleep(6000); } } void main() { //cmem(); autoptr(); autoptrnew(); cin.get(); }
13. 多元数组 tuple :
#include <iostream> #include <tuple> using namespace std; //多元数组,存取不同的数据类型 void main() { char ch = 'X'; short sh = 12; int num = 1234567; double db = 123; char *p = "calc"; tuple<char, short, int, double, char *>MyTuple(ch, sh, num, db, p); auto autov1 = get<0>(MyTuple); //多元数组不能用for(auto i:MyTuple)的方式遍历 cout << autov1 << endl; auto autov2 = get<1>(MyTuple); cout << autov2 << endl; auto autov3 = get<2>(MyTuple); cout << autov3 << endl; auto autov4 = get<3>(MyTuple); cout << autov4 << endl; auto autov5 = get<4>(MyTuple); cout << autov5 << endl; cin.get(); }
14. 左值引用与右值引用:
#include <iostream> using namespace std; //C++能用引用的情况,就别用指针 void change(int & rnum) { rnum = 111; } void main0501() { //int num(10); //num是左值,有内存实体,可以赋值 int num=10; //int & rnum(num); //引用&就是变量的别名 // //rnum = 1; //rnum等价于num的别名 //change(num); //cout << num << endl; //111 //int num = 1; //int data = 0; //cout << (void *)&data << endl; //data = num + 1; //data = num + 2; //data = num + 3; //cout << data << endl; //右值引用 int num = 1; int && rnum(num + 4); //右值引用,快速备份寄存器的值。编译器会自动回收 printf("%p\n", &rnum); //若是左值引用,需要两步: int data = num + 4; int &rdata(data); int a[5]{ 1,2,3,4,5 }; int *p(a); cout << *p << endl; //1 int * & rp(p); //左值引用改变指针 & 放在类型与变量名之间 rp += 1; cout << *p << endl; //2 int * && rrp(p + 2); //右值引用改变指针 && //int * && rrp(&a[1]); cout << *rrp << endl; //4 cin.get(); } void showit(int && rrnum) //右值引用 { cout << rrnum << endl; } void main() { int a[5]{ 1,2,3,4,5 }; showit(a[3] + 2); //移动语义:将左值转化为右值(左值一定可以变为右值,右值不一定可变为左值) showit(move(a[3])); cin.get(); }
15. 引用的本质:
#include <iostream> using namespace std; void main0601() { int num = 10; int data = 20; int & rnum(num); //引用一旦初始化,就不会再引用其他变量 rnum = data; cout << num << endl; //20 cout << data << endl; //20 cin.get(); } void main() { double db; //double & rdb; //error C2530: “rdb”: 必须初始化引用 double & rdb(db); cout << sizeof(rdb) << endl; //8 struct MyStruct //引用的本质是指针实现的,为了简化程序 { double & rdb; }; cout << sizeof(MyStruct) << endl; //4 cin.get(); }
16. 引用指针以及作为函数参数和函数返回值:
#include <iostream> using namespace std; //改变指针,需要二级指针 //C++中可用引用 void main0701() { int a(4); //初始化为4 int *p(new int(5)); //开辟int大小空间,值为5 cout << a << endl; //4 cout << *p << endl; //5 //int & ra(a); //引用变量 //int * & rp(p); //引用指针 //ra = 3; //*rp = 12; //cout << a << endl; //3 //cout << *p << endl; //12 int && rra(move(a)); //右值引用,有内存实体就直接引用,没有内存实体则新开辟内存 int * && rrp(move(p)); rra = 1; cout << rra << endl; cout << a << endl; //int 整数类型 //int & 引用整数,本质是指针 //int && 引用整数,本质是指针,能处理左值(move) 和右值 cin.get(); } int num1 = 10; int num2 = 20; void change(int * &rp) //C++中能用引用就别用指针 { rp = &num2; } void main0702() { int *p = &num1; change(p); cout << *p << endl; //20 cin.get(); } void main0703() { int *p(nullptr); int **pp(&p); int ** &rpp(pp); //VS2015 C++14 //int **pp(&p); //int(** (&rpp))(pp); //VS2013 C++11 cin.get(); } void main0704() { int ***ppp(nullptr); int *** & rppp(ppp); cin.get(); } int data1 = 10; int data2 = 20; int *p = &data1; void changeit(int ** &rpp) { *rpp = &data2; } void main0705() { int **pp = &p; //二级指针 cout << **pp << endl; //10 changeit(pp); cout << **pp << endl; //20 cin.get(); } //返回一个引用a int getdata() { int num = 10; return num; //函数返回值有副本机制 } void main0706() { cout << getdata() << endl; //10 cin.get(); } //函数返回值有副本机制,返回变量 //栈区,自动回收、释放,返回为指针不能指向栈区,返回为引用不能应用栈区 int & getdata1() { int num = 10; return num; //函数返回值有副本机制 } void main0707() { int & rnum = getdata1(); //此时引用的是原来的内存,调用后已经释放 cout << "hello" << endl; cout << rnum << endl; //此处结果并不是10 int rnum2 = getdata1(); //拷贝到新的内存rnum2中 cout << rnum2 << endl; //10 cin.get(); } int & getdata2() { int *p(new int(5)); //堆区 return *p; } void main0708() { int & rnum = getdata2(); //此时引用的是堆区 cout << "hello" << endl; cout << rnum << endl; //5 cin.get(); } char * & getcode() { char *p = "hellohellohello"; //常量字符串-->代码区(与程序供存亡) return p; //p在栈上,但指向内容在代码区 } void main() { char * &rp = getcode(); //指针,引用p,但这个p在栈上,消亡了 char * newp = rp; //保存rp存储代码区地址 cout << "hello china" << endl; cout << newp << endl; //hellohellohello cout << rp << endl; //乱码 cin.get(); }
17. 引用数组:
#include <iostream> using namespace std; void main0801() { int a[5]{ 1,2,3,4,5 }; //一维数组 int *pa[5]{ a,a + 1,a + 2,a + 3,a + 4 }; //指针数组 int b[2][3]{ 1,2,3,4,5,6 }; //二维数组 //二维数组,每一个元素是一个指针 int *pb[2][3]{ &b[0][0],&b[0][0] + 1,&b[0][0] + 2,&b[0][0] + 3,&b[0][0] + 4,&b[0][0] + 5 }; int *p1(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} }; //堆上的二维数组 cin.get(); } void main0802() { int a[5]{ 1,2,3,4,5 }; int(&ra)[5](a); //引用一维数组 for (auto i : ra) cout << i << " "; cout << endl; int *pa[5]{ a,a + 1,a + 2,a + 3,a + 4 }; int *(&rpa)[5](pa); //引用指针数组 for (auto i : rpa) cout << *i << " "; cout << endl; cin.get(); } void main0803() { int *p1(new int[5]{ 1,2,3,4,5 }); int **pp(new int *[5]{ p1,p1 + 1,p1 + 2,p1 + 3,p1 + 4 }); //堆上指针数组 int * &rp(p1); int ** &rpp(pp); cin.get(); } void main0804() { int b[2][3]{ 1,2,3,4,5,6 }; //二维数组 //二维数组,每一个元素是一个指针 int *pb[2][3]{ &b[0][0],&b[0][0] + 1,&b[0][0] + 2,&b[0][0] + 3,&b[0][0] + 4,&b[0][0] + 5 }; int(&rb)[2][3](b); //引用二维数组 int *(&rpb)[2][3](pb); //引用二维指针数组 cin.get(); } void main() { //int(*p2)[4] = (new int[3][4]{ { 1,2,3,4 },{ 5,6,7,8 },{ 9,10,11,12 } }); //堆上的二维数组 //指向数组的指针,用{}初始化 int(*p2)[4]{ new int[3][4]{ { 1,2,3,4 },{ 5,6,7,8 },{ 9,10,11,12 } } }; int(*&rp)[4](p2); //引用行指针 for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) cout << rp[i][j]<<" "; cout << endl; } int * (*pp)[4]{ new int *[3][4] }; //堆上开辟二维指针数组 int * (*&rpp)[4](pp); //引用二维指针数组 cin.get(); }