C++全总结
Posted timeobjserver
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++全总结相关的知识,希望对你有一定的参考价值。
1 // CPPTEST.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include<iostream> 6 #include <map> 7 #include<fstream> 8 #include<cassert> 9 #include <sstream> 10 #include"TMyNumOperator.h" 11 #include"abc.h" 12 #include <list> 13 #include<thread> 14 #include <vector> 15 #include <algorithm> 16 using namespace std; 17 using std::cin; 18 using std::cout; 19 20 //using namespace std; 21 // 22 //class CBase{ 23 //protected://注意,可以使用C#风格的定义时初始化 24 // std::string name = "NoOne"; 25 // int age = -20; 26 // int sex = 1; 27 //public: 28 // float *pData = new float[20]{1, 2, 3, 4, 5}; 29 //public: 30 // virtual ~CBase(){//虚析构函数,防止内存泄漏:对基类指针调用delete时,会从子类一直析构到基类 31 // cout << "~cbase" << endl; 32 // } 33 //}; 34 // 35 ////基类的私有成员不会被继承,这和C#完全一样 36 //class CStudent : public CBase{ 37 //public: 38 // std::map<int, std::string> _projs; 39 // CStudent(){ 40 // pData = new float[20]{1, 2, 3, 4, 5}; 41 // } 42 //public: 43 // void SetName(const std::string& name){ 44 // CBase::name = name;//如果CBase.name定义为私有,这里就不可访问 45 // this->name = name; //等价于上一行 46 // } 47 // 48 // const string& GetName(){ 49 // return this->name; 50 // } 51 // 52 // ~CStudent(){//若采用浅拷贝,析构函数被调用多次,pData被删除多次,程序崩溃 53 // //规避方式:判断pData是否为空,非空才delete[] pData 54 // //但在不知情的情况下使用pData仍然会出问题,因此浅拷贝导致的问题不可规避 55 // cout << "~cstudent" << endl; 56 // delete[] pData; 57 // pData = NULL; 58 // } 59 //}; 60 // 61 //void TestSTL(){ 62 // 63 // auto mp = new std::map<int, std::string>();//c++11新风格 auto 64 // 65 // mp->insert({ 10, ("h你好") });//c++11新风格,不用再使用std::pair()或std::make_pair() 66 // mp->insert({ 20, "el" }); 67 // for (auto var : *mp)//c++11新风格for 68 // { 69 // std::cout << var.first << "," << var.second << "," << std::endl; 70 // } 71 //} 72 // 73 //void TestClass(){ 74 // CBase* pbase = new CStudent(); 75 // auto pst = (CStudent*)pbase; 76 // pst->SetName("xxxx"); 77 // auto name = pst->GetName(); 78 // delete pbase; 79 //} 80 // 81 //int TestAdd(int a, int b){ 82 // return a + b; 83 //} 84 //void TestStdFunc(std::function<int(int,int)> fun, int a, int b){ 85 // auto ret = fun(a, b); 86 //} 87 // 88 //typedef int(*TestAddPtr)(int, int); 89 // 90 //void TestPCall(TestAddPtr func, int a, int b){ 91 // auto ret = func(a, b); 92 //} 93 // 94 //struct Vertex{ 95 // bool isgood; 96 // float x, y, z; 97 // double dx; 98 // bool bx; 99 // int ix; 100 // bool by; 101 //}; 102 //void TestFile(){ 103 // int szChar = sizeof(char); 104 // ofstream ofs; 105 // ofs.open("f:/test.txt"); 106 // ofs << "hello " << 10 << " world " << 20 << endl; 107 // 108 // ofs.flush(); 109 // ofs.close(); 110 // 111 // ifstream ifs; 112 // ifs.open("f:/test.txt"); 113 // string str1, str2; 114 // int num1, num2; 115 // 116 // ifs >> str1 >> num1 >> str2 >> num2; 117 // 118 // //错误示例:二进制读写,使用std::<<或>>进行的还是ASCII码的读写 119 // ofstream ofsb; 120 // ofsb.open("f:/testb", ios::binary); 121 // ofsb << "hellob" << 1022; 122 // 123 // ofsb.flush(); 124 // ofsb.close(); 125 // ifstream ifsb; 126 // 127 // string sx; 128 // int nx; 129 // ifsb.open("f:/testb", ios::binary); 130 // ifsb >> sx >> nx; 131 // ifsb.close(); 132 // 133 // //正确做法 134 // sx = "binary"; 135 // nx = 978; 136 // ofsb.open("f:/testbx", ios::binary); 137 // ofsb.write(sx.c_str(), sx.length()*sizeof(char)+1); 138 // ofsb.write((const char*)&(nx), sizeof(int)); 139 // ofsb.flush(); 140 // ofsb.close(); 141 // 142 // char sxr[32]; 143 // int nxr; 144 // ifsb.open("f:///testbx", ios::binary);//注意这里的"///"不管有多少个/都等同于一个 145 // ifsb.read(sxr, sx.length()+1); 146 // ifsb.read((char*)&nxr, 4); 147 // 148 // //数据转换的更通用方式 149 // Vertex vt; 150 // vt.bx = true; 151 // vt.isgood = false; 152 // vt.x = 12; 153 // vt.y = 13; 154 // vt.z = 14; 155 // vt.dx = 3.9; 156 // vt.by = 0; 157 // 158 // ofstream ofsbx; 159 // ofsbx.clear(); 160 // ofsbx.open("f:/testbyx2", ios::binary); 161 // ofsbx.write((const char*)&(vt), sizeof(Vertex)); 162 // ofsbx.flush(); 163 // ofsbx.close(); 164 // 165 // ifstream ifsbx; 166 // Vertex vrt; 167 // ifsbx.clear(); 168 // ifsbx.open("f:/testbyx2", ios::binary); 169 // ifsbx.read((char*)&vrt, sizeof(Vertex)); 170 // 171 // string s1 = "hello"; 172 // string s2 = "wrold"; 173 // s1 = s1 + s2; 174 // auto s3 = s1.substr(1, 2); 175 //} 176 // 177 ////实现较为高效的字符串分割,限制是分割符只能是一个字符,不能是一个串 178 //std::list<string> TestStrSplit(string s, char sep){ 179 // std::list<string> lst; 180 // for (int i = 0, j = 0; i < s.length(); ++i){ 181 // if (s[i] == sep){ 182 // lst.push_back(s.substr(j, i - j)); 183 // j = i + 1; 184 // } 185 // } 186 // 187 // //注意临时对象作为返回值了,一般情况下这是错误的用法,栈上的临时对象出了函数域后会被释放 188 // //但这里STL容器内部重载了=运算符,作了值拷贝就没问题了 189 // return lst; 190 //} 191 //void TestString(){ 192 // 193 // //g正则表达式实现字符串分割 194 // string s1 = "a;b;c;dddd;ef;"; 195 // string s2 = "a123b2673cdd4444a"; 196 // std::regex re("(d+)"); 197 // std::smatch mtch; 198 // 199 // //这个做法效率挺低且浪费内存,产生了很多中间字符串 200 // while (std::regex_search(s2, mtch, re, std::regex_constants::match_default)){ 201 // cout << mtch.str() << endl; 202 // s2 = mtch.suffix(); 203 // } 204 // 205 // //这个函数效率要高多了 206 // auto lst = TestStrSplit(s1, ‘;‘); 207 // 208 //} 209 // 210 ////返回栈上的临时对象测试 211 //CStudent TestTempObjRet(){ 212 // CStudent ost; //临时对象 213 // return ost; //调用对象的拷贝构造函数 214 //}//出了栈后ost被释放,析构函数调用,同时成员对象被析构CStudent.name="",但内置类型仍保持原值 215 // 216 ////通过测试可知,将栈上对象作为函数返回值使用一般是没有问题的,但浅COPY时两个对象中的指针指向同一份 217 ////内存,当一个对象被删除时,另一个对象中的指针就指向非法位置了,成了野指针 218 //void TestObjConstructorAndDestructor(){ 219 // CStudent ostx; 220 // ostx = TestTempObjRet(); //调用拷贝构造函数(与上面对应) 221 // auto name = ostx.GetName(); 222 // auto px = ostx.pData; 223 //} 224 // 225 //void TestRRef(){ 226 // 227 //} 228 // 229 ////可以使用随机访问(数组下标)说明vector在内存中是连续存放的 230 ////这样,vector在需要扩充容量时就需要将原来内存删除,再申请一块新内存 231 ////但这并不一定,因为内存申请时若用realloc则有可能会在原内存后面增加(原理) 232 //void TestVector(){ 233 // std::vector<string> sv{ "hello", "world" }; 234 // sv[0]; 235 // sv[1]; 236 // 237 // sv.reserve(20); //旧的内容被清除 238 // int n = sv.capacity(); //20 239 // sv.push_back("a"); 240 // sv.push_back("b"); 241 // sv.clear(); //旧的内容被清除 242 // n = sv.capacity(); //20 243 // 244 // sv.shrink_to_fit(); //内存释放 245 // n = sv.capacity(); //0 246 // 247 //} 248 // 249 //struct CTA{ 250 //private: 251 // virtual void Test(){ 252 // cout << "cta" << endl; 253 // } 254 // 255 //}; 256 // 257 //class CCTA : CTA{//类和结构体可以相互继承 258 //public: 259 // int _id; 260 // void Test() const{ 261 // cout << "ccta-test" << endl; 262 // } 263 //}; 264 // 265 ////C++中字符串有常量和变量之分,字符串遇到 则结束 266 ////C#中只有常量字符串,字符串遇到 不结束,视其为正常字符 267 //void TestStr(){ 268 // char* ps = "hello";//字符串常量,不可修改其内容 269 // ps[0] = ‘d‘; //运行出错 270 // 271 // char arr[] = "hello"; //字符串变量 272 // char* par = arr; 273 // arr[0] = ‘d‘; //ok 274 //} 275 // 276 ////C++中指针字符串与数组字符串都是自动以0结尾的 277 //void TestMemcpy(){ 278 // 279 // char dest[18]; 280 // char src[] = "hell"; //以0结尾,长度为5,若强制声明为 char src[4] = "hell"则编译报错 281 // char* psrc = "hell"; //以0结尾,长度为5,但测试长度strlen(psrc)为4,因为它没算尾0 282 // 283 // for (int i = 0; i < 10; ++i){ 284 // 285 // } 286 // for (int i = 0, ch; (ch = psrc[i++]) != 0;){ 287 // //这里发现字符串尾0后有许多个0,不知道原因 288 // 289 // } 290 // auto len = strlen(psrc); //4,测试长度,并没字符串的真实长度(内存中真实串),因为它有尾0 291 // int len2 = strlen(src); //5,字符串实际长度(内存中存储的字符串) 292 // int st = sizeof(src); //5,数组大小 293 // memcpy(dest, psrc, strlen(psrc)+1); 294 //} 295 //template<typename T1, class T2> class MyVector{ 296 // std::vector<int> _lst; 297 // 298 //public: 299 // 300 // void Test2(); 301 //}; 302 // 303 //template<class T1, class T2> void MyVector<T1, T2>::Test2(){ 304 // 305 //} 306 307 #pragma region 2018.7.7 308 [module(name = "mytestx")]; 309 void TestIOStream() { 310 std::fstream fs; 311 fs.open("test.txt", ios_base::in | ios_base::out); 312 fs << 12 << "hello"; 313 314 fs.seekp(0); 315 int ix1; 316 string sx1; 317 char chs[6]; 318 fs >> ix1; 319 fs >> chs; 320 chs[5] = 0; 321 sx1 = chs; 322 323 cout << ix1 << sx1.c_str() << endl; 324 325 } 326 void TestMacro() { 327 #define hfunc(x) cout << x << endl; //自定义处起,全局可见 328 hfunc(124); 329 #undef hfunc 330 331 //typedf, using等价使用 332 typedef void(*PFUN)(int); 333 using PFUNC = void(*)(int); 334 335 using Int = int; 336 using MyType = Int; 337 } 338 //数组和指针 339 void TestArrayAndPointer() { 340 //1,char* p : char类型指针,指向char数组, p++移动一个char 341 //2,int* p : int型指针,指向int数组,p++移动一个int 342 //3,char(*p)[2] : char[2]类型指针,指向char[2]类型数组,即char[][2]数组,p++移动一个char[2] 343 //总结:X类型的指针指向X类型的数组, p++移动一个数组元素 344 //如何看指针类型:去除*p剩下的就是类型,如char*p去掉*p为char,char(*p)[2]去掉*p为char[2] 345 346 //======================================================== 347 //指针总是指向数组的,如下,可认为是指向只有一个元素的数组 348 //======================================================== 349 int ix = 20; 350 int*pix = &ix; 351 cout << pix[0] << "," << *pix << endl; 352 353 //======================================================================================== 354 //堆和栈上数组的初始化列表写法 355 //======================================================================================== 356 char arr[43] = { ‘a‘,‘b‘,‘c‘ }; 357 char arr2[10] = { "hello" }; 358 int iarr[] = { 1, 2, 3, 4 }; 359 char*ps = new char[30]{ 0 }; 360 int* ips = new int[30]{}; 361 int* ips2 = new int[30]; 362 363 //cout << arr << "," << (void*)arr << (void*) ps << endl; 364 char* px; 365 px = arr; //可以赋值,说明数组名与指针等价 366 const char* cp;//可以cp++; 367 char* const cpx = arr; //不可以 cpx++,不能移动的指针,数组名其实就是这种指针 368 369 //这里以arr与ps作对比,数组名与指针本质上都是指针,只是数组名是不能移动,不能赋值的常指针 370 //在二维情形时也是如此 371 372 373 stringstream ss; 374 //======================================================================================== 375 //1,栈上二维数组,【内存连续】 376 //======================================================================================== 377 char a[][3] = {//二维数组初始化列表 378 { 98, 99, 100 }, 379 { 101, 102, 103 }, 380 }; 381 for (int i = 0; i < 6; ++i) {//验证 382 ss << *(*a + i) << ","; 383 } 384 cout << ss.str() << endl; 385 386 //============================================================================= 387 //2,数组指针(也称行指针),【内存连续】 388 //============================================================================= 389 int(*pax)[4] = new int[3][4]; 390 for (int i = 0; i < 3; ++i) { 391 for (int j = 0; j < 4; ++j) { 392 pax[i][j] = i * 4 + j + 1; 393 } 394 } 395 396 ss.str(""); 397 for (int i = 0; i < 12; ++i) {//验证 398 ss << *(*pax + i) << ","; 399 } 400 cout << ss.str() << endl; 401 402 //============================================================================= 403 //3,指针数组,【内存不连续】 404 //============================================================================= 405 //因为它是一个数组,所以不能用new来给它分配内存,new出来的东西只能赋值给指针 406 char* arr_p[2]; 407 arr_p[0] = new char[30]{ ‘h‘,‘e‘,‘o‘,‘l‘,‘l‘ }; 408 arr_p[1] = new char[10]{ ‘a‘,‘b‘,‘c‘ }; 409 410 411 //============================================================================= 412 //4,多级指针用来分配二维数组,有【连续内存分配法】和【不连续内存分配法】 413 //这个非常重要,若用一个不连续的二维数组指针进行memcpy操作,则会发生严重问题: 414 //(1)数据拷越界,覆盖了其它变量甚至程序的内存 415 //(2)dest变量中数据只填充了一部分,其余部分还是旧数据,导致程序出现莫名其妙的问题 416 //(3)这种数据拷越界并无任何提示,隐蔽性极高,非常难以查找 417 //============================================================================= 418 int**pi = new int*[3]; 419 int* ptemp = new int[12]; 420 for (auto i = 0; i < 3; ++i) { 421 //------------------------------------------------ 422 //(1)【不连续内存分配法】 423 //pi[i] = new int[2]; 424 425 //------------------------------------------------ 426 //(2)【连续内存分配法】 427 pi[i] = &((ptemp + i * 2)[0]); 428 for (int j = 0; j < 2; ++j) { 429 pi[i][j] = i * 2 + j; 430 } 431 } 432 for (int i = 0; i < 3; ++i) {//验证 433 for (int j = 0; j < 2; ++j) 434 { 435 ss << pi[i][j] << ","; 436 } 437 } 438 cout << ss.str() << endl; 439 440 } 441 void TestInitialist() { 442 class CIn { 443 public: 444 float x, y, z; 445 string name; 446 447 }; 448 449 //初始化列表的使用条件: 450 //无自定义构造函数,成员公有,无基类,无虚函数 451 //这么多限制,可以说很鸡肋 452 CIn oin = { 1, 2, 3, "hello" }; //方式1 453 CIn oin2 { 1, 2 ,3, "world" }; //方式2 454 } 455 #pragma endregion 456 457 #pragma region 2018.7.9 458 class CComplex { 459 float real, image; 460 public: 461 CComplex(float real, float image) { 462 cout << "constructor: " << real << "," << image << endl; 463 this->real = real; 464 this->image = image; 465 } 466 467 CComplex(const CComplex& other) { 468 cout << "copy constructor: " << other.real << "," << other.image << endl; 469 if (this != &other) 470 { 471 real = other.real; 472 image = other.image; 473 } 474 } 475 ~CComplex() { 476 cout << "~ccomplex" << "(" << real <<"," <<image << ")" << endl; 477 // real = 0; 478 // image = 0; 479 480 } 481 482 void PrintInfo() { 483 cout <<"Complex: " << real << "," << image<< endl; 484 } 485 486 public: 487 488 //------------------------------------------- 489 //运算符重载 490 //------------------------------------------- 491 //1,重载为成员函数 492 CComplex operator+(const CComplex& other) { 493 cout << "operator+" << endl; 494 return CComplex(real+other.real, image + other.image); 495 } 496 497 CComplex& operator++() {//前向++ 498 cout << "forward ++ " << endl; 499 real++; image++; 500 return *this; 501 } 502 CComplex& operator++(int) {//后向++ 503 cout << "backward ++ " << endl; 504 505 real++; image++; 506 return *this; 507 } 508 const CComplex& operator=(const CComplex& other) { 509 this->real = other.real; 510 this->image = other.image; 511 return *this; 512 } 513 //2,重载为友元函数 514 friend CComplex operator+(float fx, const CComplex& cp); 515 516 //3,【运算符重载函数不能定义为静态函数】 517 //这与C#不同,C#中所有运算符重载都必须是public和static的 518 //static CComplex operator+(float fx, const CComplex& cp); 519 520 //4,类型转换运算符重载 521 operator bool() {//使用情景:CComplex oc; if(oc){}或if(oc != NULL){}或 float/int/bool x = oc 522 return real != 0 && image != 0; 523 } 524 operator float() {//使用情景:CComplex oc; if(oc){}或if(oc != NULL){}或 float/int/bool x = oc 525 return real; 526 } 527 528 // CComplex operator=(const CComplex& other) { 529 // if (this == &other) 530 // return other; 531 // return CComplex(other.real, other.image); 532 // } 533 534 void Testx() { 535 CComplex* pNewCom = new CComplex(2, 2); 536 pNewCom->real = 20;//可以访问私有成员?? 537 } 538 }; 539 // CComplex CComplex::operator+(float fx, const CComplex& cp) { 540 // return CComplex(fx + cp.real, cp.image); 541 // } 542 CComplex operator+(float fx, const CComplex& cp) { 543 return CComplex(fx + cp.real, cp.image); 544 } 545 546 void TestCComplexOper() { 547 int i = 10; 548 CComplex cpx(1, 2); 549 ++cpx++++; 550 cpx.PrintInfo(); 551 } 552 CComplex TestReturnStackObj() { 553 //----------------------------------------------------------------- 554 //返回栈上的对象 stackObj 555 //返回栈上的对象会导致拷贝构造函数的调用,生成一个 556 CComplex stackObj(1, 2); 557 return stackObj; 558 559 return CComplex(1, 2); //这种方式直接调用构造函数,而不调用拷贝构造函数 560 //----------------------------------------------------------------- 561 } 562 563 #pragma endregion 564 565 #pragma region 2018.7.10 566 void TestRealloc() { 567 cout << "---------------test-realloc---------------" << endl; 568 569 int szch = sizeof(char); 570 char*pstr = "this is a test str"; 571 int strLen = strlen(pstr); 572 573 char* pdesc = (char*) malloc((1+strLen)* sizeof(char)); 574 for (int i = 0; i < strLen; ++i) { 575 cout << "," << hex<< (int)pdesc[i]; 576 } 577 cout << endl; 578 579 cout << strlen(pstr) << endl; 580 581 strcpy_s(pdesc, strLen+1, pstr); 582 583 for (int i = 0; i < strLen; ++i) { 584 if(pdesc[i] > 0) 585 cout << (char)pdesc[i]; 586 else cout << "," << (int)pdesc[i] ; 587 } 588 589 cout << endl; 590 591 pdesc = (char*)realloc(pdesc, 40); 592 for (int i = 0; i < 40; ++i) { 593 pdesc[strLen + i] = ‘a‘ + i; 594 } 595 596 for (int i = 0; i < 40 + strLen; ++i) { 597 if (i < strLen) 598 cout << pdesc[i] << ","; 599 else 600 cout << (unsigned short)pdesc[i] << ","; 601 } 602 cout << endl; 603 604 cout << "---------------test-realloc---------------" << endl; 605 } 606 607 template<typename T> class CMyNumOperator { 608 T a, b; 609 public: 610 static T Add(T x, T y) { 611 return x + y; 612 } 613 }; 614 #pragma endregion 615 616 #pragma region 2018.7.11 617 #pragma region 继承相关 618 class A { 619 public: 620 A(int x) { 621 fProtected = x; 622 } 623 float GetFProtected() { 624 return fProtected; 625 } 626 627 public: 628 float fpublic = 2.3f; //c++11支持了初始化,但不能使用auto 629 string sname = "liqi"; 630 CMyNumOperator<int>* on = new CMyNumOperator<int>(); //对象也可以 631 632 void TestFunc() { 633 cout << "TestFunc" << endl; 634 } 635 636 static void StaticTestFunc() { 637 cout << "Static-TestFunc" << endl; 638 } 639 virtual void ToString() { 640 cout << "A::ToString" << endl; 641 } 642 protected: 643 float fProtected; 644 void ProtectedFunc() { 645 cout << "PRotectedFunc" << endl; 646 } 647 private: 648 void PrivateFunc() { 649 cout << "PrivateFunc" << endl; 650 651 } 652 653 }; 654 655 //只管公有继承,不管保护继承和私有继承,意义不大,也太复杂 656 class B : public A { 657 public: 658 friend void TestProtectedDerive(); 659 B() :A(1) {} 660 void TestForDerive() { 661 //公有继承下 662 //1,子类可以访问父类的保护成员,不能访问父类的私有成员 663 B ob; 664 //PrivateFunc(); //error,子类不能访问基类的私有成员 665 ProtectedFunc(); //right 666 fProtected = 10; //right 667 ob.fProtected = 20; //right 668 } 669 670 //1,c++中只要基类有相同签名虚函数,则默认为此基类函数也是虚函数[与C#不同],如下情形都成立 671 // (1) 函数不声明 virtual 672 // (2) 函数声明了 virtual 673 // (3) 函数声明了 override 674 // (4) 函数声明了 virtual 和 override 675 //2,c++中两个关键词作用不同,可以同时存在 676 // virtual仅表明函数是虚函数,override是C++11中出现的,明确说明是对基类的重写 677 // 它的好处是当函数声明不符合规则时,编译器会报错 678 void virtual ToString() override{ 679 cout << "B::ToString" << endl; 680 } 681 }; 682 683 void TestProtectedDerive() { 684 B ob; 685 ob.ProtectedFunc(); 686 } 687 688 #pragma endregion 689 #pragma endregion 690 #pragma region 2018.7.18 691 #pragma region 标准输入流 692 void TestCinCout() { 693 float fx; 694 std::string str; 695 while (true) { 696 bool errorNum = false; 697 cin >> str; //1,试读,看是不是"exit"串 698 if (str == "exit")//2,若是,结束循环 699 break; 700 for (int i = str.length() - 1; i >= 0; --i) {//3,若不是,将串放回到流中,注意是反向放回的 701 cin.putback(str[i]); 702 } 703 704 cin >> fx; 705 if (cin.fail()) {//4,如果格式错误 706 cout << "格式错误:请输入一个数值" << endl; 707 cin.clear(); //5,清除错误标识 708 while (cin.get() != ‘ ‘); //6,读掉后面出错的所有字符,直到回车 709 errorNum = true; 710 } 711 712 if (!errorNum) {//7,若前面输入(数字)是正确的,则继续后面的解析 713 cin >> str; 714 if (cin.fail()) { 715 cout << "格式错误:请输入一个字符串" << endl; 716 cin.clear(); 717 } 718 cout << ">>数值= " << fx << ", 描述= " << str << endl; 719 } 720 721 } 722 723 } 724 #pragma endregion 725 #pragma region 计算机数据存储 726 void TestComputeDataStorage() { 727 //数据转换:C++,C# 通用 728 //1,整形数据:短数据类型转长数据类型时,正数高位补0,负数高位补1 729 //2,浮点形数据转整形时,得到整数部分,舍去了小数部分 730 731 cout << hex; 732 cout << (int)(short)1 << endl; //1,即 0x00000001 733 cout << (int)(short)-1 << endl; //0xffffffff,即负数高位补1 734 cout << -1 << endl; //0xffffffff,负数表示法,符号位1,真值(1)求补码 735 736 auto sz = sizeof(long);//64位系统,X64编译器下VS2017测试值为4 737 float fx = 83.7f; 738 auto lfx = (long unsigned int)fx; //浮点转整形, 739 long long x; //8位整形 740 long unsigned int lui; //8位无符号整形 741 742 //浮点数据字节察看 743 //125.5f = 0x42fb0000 744 //-125.5f = 0xc2fb0000 745 //83.7f = 0x42a76666 746 //浮点数存储按IEEE754标准: 747 //以float为例:共4个字节,从高位到低位依次是31,30,...2,1,0 748 //最高位存放数据符号,接下来8位存放阶码(包括阶码符号位),接下来23位存放尾数 749 int ifx = *(int*)(&fx); 750 //等价于 751 int* pfx = (int*)&fx; 752 int ipfx = *pfx; 753 754 int sz2 = sizeof(x); 755 } 756 757 #pragma endregion 758 #pragma region 地址与指针 759 void TestAddrAndPointer() { 760 //------------------------------------------------------------- 761 //1,&p, p, *p的区别: &p是p的地址,p是一个地址,*p是地址中的内容 762 //2,地址与指针完全等价,有两种操作:*地址,地址-> 763 //3,地址就是一个数值,指针也是个地址 764 int x = 10; 765 *(&x) = 0x100; 766 *((char*)&x) = 1; //小端模式下[低字节存低地址处,高字节存高地址处]:0x101 767 int* pxt = (int*)10; //直接指向内存地址0x0000000a处 768 int*px = &x; //px与 &x完全等价 769 int adr = (int)(&x); //地址就是个数值,指针也是个地址值 770 px = (int*)adr; 771 772 cout << hex; //输出为16进制 773 cout << adr << "," << &x << "," << (int*)&x << "," << px << endl; //四者等价,输出相同值 774 cout << dec; //输出为10进制 775 776 A oa(0); 777 (&oa)->fpublic = 30; //地址与指针等价 778 (*(&oa)).fpublic = 111; //地址与指针等价 779 780 } 781 #pragma endregion 782 #pragma region 函数指针 783 void TestFuncPtr() { 784 cout << "TestFuncPtr" << endl; 785 } 786 void TestFuncPtrParam(int, int, int) {//注意函数参数可以不写变量名 787 void(*pf)(int, int, int) = TestFuncPtrParam; 788 int*p = (int*)pf; 789 790 //试图找出函数实参,失败,对函数汇编原理不清楚,有时间再查 791 cout << *(p) << "," << *(p-1) << endl; 792 } 793 void TestFuncPointer() { 794 A oa(0); 795 //1,函数指针与普通指针不兼容,不能相互强转 796 //2,函数指针赋值方式有二:pf = func或 pf = &func 797 //3,函数指针pf使用方式有二:pf()或 (*pf)(),因为pf和 *pf的值相同,调试模式下可以看到 798 799 //1,普通成员函数指针 800 typedef void(A::* PFUNC)(void); //函数指针声明方式一 801 using PFunc = void(A::*)(void); //函数指针声明方式二,C++11新方式 802 803 PFunc pf = &(A::TestFunc); 804 int pfsz = sizeof(pf); 805 (oa.*pf)(); 806 807 //2,全局函数指针 808 void(*pfg)() = TestFuncPtr; 809 pfg(); 810 (*pfg)(); 811 812 //3,静态函数指针 813 void(*sptf)() = A::StaticTestFunc; 814 sptf(); 815 (*sptf)(); 816 } 817 #pragma endregion 818 #pragma region 虚函数表原理 819 void TestVirtualFunctionTable() { 820 cout << hex; 821 typedef void(*PFUNC)(); 822 823 offsetof(A, fpublic); //利用此函数可以算函数布局 824 825 A oa(0); 826 B ob; 827 828 //一,通过内存地址修改不可访问的保护变量 829 *(float*)((int*)&oa + 1) = 123.4f; //类的第一个变量fpublic赋值,(int*)&oa + 1是跳过虚函数指针 830 float fpublic = oa.fpublic; 831 832 //二,通过内存地址调用虚函数 833 //A和B的虚函数表地址不一样,也就是说父类和子类各有一张虚函数表 834 int* pvptr = (int*)(*((int*)(&oa))); 835 cout << "A的虚函数表地址:" << pvptr << endl; //000DB0D4 836 ((void(*)())(*pvptr))(); //A::ToString 837 838 pvptr = (int*)(*((int*)(&ob))); 839 cout << "B的虚函数表地址:" << pvptr << endl; //000DB128 840 ((void(*)())(*pvptr))(); //B::ToString 841 842 843 cout << "--------------------------" << endl; 844 //最简写法 845 ((void(*)())(*((int*)*(int*)&oa)))(); 846 ((void(*)())(*((int*)*(int*)&ob)))(); 847 848 } 849 #pragma endregion 850 #pragma region 函数对象,友元函数模板运算符重载 851 template<class T> 852 class AddTwoNumber { 853 public: 854 T x; 855 856 AddTwoNumber(T x) { 857 this->x = x; 858 } 859 public: 860 //【通过重载()运算符,实现函数对象】 861 T operator()(T a, T b) { 862 return a + b; 863 } 864 865 //一,使用模板类型的友元模板函数 866 //1, <>表示该友元是一个模板函数,且使用本模板类的类型 867 // 若不加<>说明符,则找不到模板函数定义,运行时出错 868 //2,这里的T是模板类传来的类型,因此,这里不能实现与T不同的类型操作 869 //比如若T为int,则 2.1f + new AddTwoNumber<int>()不合法 870 //3,【注意这里第二个参数是个引用类型,若是AddTwoNumber<T>对象类型则会出错,不能在类中定义本类对象】 871 friend void operator+ <>(T os, AddTwoNumber<T>& n); 872 873 //二,使用模板函数自带类型的友元模板函数 874 //这里的T是一个新的类型,与此模板类的T没关系,因此没有上面的限制 875 template<class T> 876 friend void operator+(T os, A oa); 877 878 template<class T> 879 T Add(T a, T b); 880 }; 881 882 template<class T> 883 void operator+ <>(T os, AddTwoNumber<T>& n) { 884 cout << "operator+: n + AddTwoNumber: " << os << endl; 885 } 886 887 template<class T> 888 void operator+(T n, A o) { 889 cout << "operator+: n + A : " << n << endl; 890 } 891 892 //================================================== 893 //※※※※※※注意这种多层的模板前置声明※※※※※※※ 894 template<typename T> //类模板的前置声明 895 template<typename T1> //函数模板的前置声明 896 T1 AddTwoNumber<T>::Add(T1 a, T1 b) { 897 return a + b; 898 } 899 900 void TestAdd2Num() { 901 AddTwoNumber<double> NumAdd(1); 902 auto nadd = NumAdd(1, 2); 903 A oa(1); 904 2.1f + oa; //左操作数任意数值类型,因为使用的是模板函数自带类型 905 2.0 + NumAdd;//左操作数必须为double, 906 907 AddTwoNumber<string> add2("str"); 908 add2.Add(1, 1); 909 cout << "x: " << add2.x << endl; 910 } 911 #pragma endregion 912 #pragma endregion 913 #pragma region 2018.7.19 914 #pragma region 智能指针 915 916 //---------------------------------------------------------------------------------------------- 917 918 template<typename T> 919 class SmartPointerx { 920 private: 921 T * _ptr; 922 size_t* _count; 923 public: 924 SmartPointerx(T* ptr = nullptr) : 925 _ptr(ptr) { 926 if (_ptr) { 927 _count = new size_t(1); 928 } 929 else { 930 _count = new size_t(0); 931 } 932 } 933 934 SmartPointerx(const SmartPointerx& ptr) { 935 if (this != &ptr) {//永远成立 936 this->_ptr = ptr._ptr; 937 this->_count = ptr._count; 938 (*this->_count)++; 939 } 940 } 941 942 SmartPointerx& operator=(const SmartPointerx& ptr) { 943 if (this->_ptr == ptr._ptr) { 944 return *this; 945 } 946 947 if (this->_ptr) { 948 (*this->_count)--; 949 if (this->_count == 0) { 950 delete this->_ptr; 951 delete this->_count; 952 } 953 } 954 955 this->_ptr = ptr._ptr; 956 this->_count = ptr._count; 957 (*this->_count)++; 958 return *this; 959 } 960 961 T& operator*() { 962 assert(this->_ptr == nullptr); 963 return *(this->_ptr); 964 965 } 966 967 T* operator->() { 968 assert(this->_ptr == nullptr); 969 return this->_ptr; 970 } 971 972 ~SmartPointerx() { 973 (*this->_count)--; 974 if (*this->_count == 0) { 975 delete this->_ptr; //数组内存泄漏 int*p = new int[10] 976 delete this->_count; 977 } 978 } 979 980 size_t use_count() { 981 return *this->_count; 982 } 983 }; 984 985 void TestSmartPtr() { 986 { 987 SmartPointerx<int> sp(new int(10)); 988 SmartPointerx<int> sp2(sp); 989 SmartPointerx<int> sp3(new int(20)); 990 sp2 = sp3; 991 std::cout << sp.use_count() << std::endl; 992 std::cout << sp3.use_count() << std::endl; 993 } 994 //delete operator 995 } 996 //---------------------------------------------------------------------------------------------- 997 998 //下面是一个简单智能指针的demo。智能指针类将一个计数器与类指向的对象相关联, 999 //引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1; 1000 //当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数; 1001 //对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象), 1002 //并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。 1003 //智能指针就是模拟指针动作的类。所有的智能指针都会重载->和 * 操作符。 1004 //智能指针还有许多其他功能,比较有用的是自动销毁。 1005 //这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存 1006 template<class T> 1007 class SmartPointer final{ //final 1008 T* pobj = NULL; 1009 __int64* refCnt = 0; 1010 public: 1011 SmartPointer(T* pobj) {//这里可能会传一个栈对象地址 1012 if (pobj) { 1013 if (pobj != this->pobj) { 1014 if (!this->pobj) 1015 this->pobj = new __int64; 1016 (*refCnt)++; 1017 this->pobj = pobj; 1018 } 1019 } 1020 } 1021 1022 SmartPointer(const SmartPointer<T>& rhs) { 1023 operator=(rsh); 1024 } 1025 1026 SmartPointer<T> operator=(const SmartPointer<T>& rhs) { 1027 if (this == &rhs || pobj == rhs.pobj) 1028 return rhs; 1029 (*refCnt)--; 1030 (*rhs.refCnt)++; 1031 pobj = rhs.pobj; 1032 return *this; 1033 } 1034 1035 ~SmartPointer() 1036 { 1037 refCnt--; 1038 if(refCnt == 0) 1039 ReleaseRes(); 1040 } 1041 1042 T* GetPtr() const { 1043 return pobj; 1044 } 1045 1046 private: 1047 void ReleaseRes() { 1048 if (pobj) { 1049 try 1050 { 1051 delete[] pobj; 1052 pobj = NULL; 1053 } 1054 catch (const std::exception&) 1055 { 1056 cout << "智能指针指向的不是一个堆对象" << endl; 1057 } 1058 } 1059 } 1060 }; 1061 #pragma endregion 1062 #pragma endregion 1063 1064 #pragma region 2018.7.23 1065 #pragma region 数组传参方式 1066 1067 //方式一,数组引用传递 1068 template<int N> 1069 void ArrayRefAsParam(char(&_dest)[N]) {//数组引用的写法 1070 char chs[] = "hello"; 1071 char* pstr = "hello"; 1072 cout << sizeof(chs) << endl; 1073 cout << strlen(chs) << ", " << strlen(pstr) << endl; 1074 1075 strcpy_s(chs, "world"); 1076 cout << chs << endl; 1077 } 1078 1079 //方式二,指针传递 1080 void PointerAsParam(const char* pArr, int elemCount) { 1081 } 1082 1083 void TestAstrPstr() { 1084 char chs[] = "world"; //6个字符,自动加了一个尾0 1085 1086 //1,数组引用传参,以下两种方式等价 1087 ArrayRefAsParam(chs); //模板不仅可以推导类型,也可以推导出数组的大小 1088 ArrayRefAsParam<6>(chs); //说明了模板的工作原理,可以不写6,模板自动推导出数组大小 1089 1090 //2,指针传递 1091 int sz = sizeof(chs); //6 1092 int slen = strlen(chs); //5 1093 PointerAsParam(chs, 1 + strlen(chs)); 1094 } 1095 #pragma endregion 1096 #pragma region 静态(变量与函数)与常量(常引用,常指针,常函数) 1097 class CWithConstStatic { 1098 private: 1099 static int _privateId; 1100 public: 1101 string _str = "CWithStatic";//C++11,可以这样初始化 1102 static string _sstr; //静态变量不允许在类内初始化,这与旧C++一致 1103 int _id = 1010; 1104 public: 1105 static void StaticMethod(){ 1106 //1,静态函数本质上是一个全局函数 1107 //2,静态函数不能访问非静态变量和非静态函数,包括常函数及常量,因为它不属于对象,没有this指针,编译器翻译时出错 1108 // _id = 10; //不访问非静态变量,因为没有this指针,不翻译为this->_id 1109 //ConstMethod();//不能访问非静态函数,因为没有this指针,不翻译为 this->ConstMethod() 1110 } 1111 void ConstMethod() const {//1 1112 auto id = this->_id; 1113 StaticMethod(); //可以访问静态函数,因为静态函数不可能更改对象 1114 //NormalMethod(); //不能访问普通函数,因为普通函数可能会更改对象 1115 } 1116 1117 void ConstMethod() { 1118 //注意1和2的两个ConstMethod函数是重载关系 1119 } 1120 1121 void NormalMethod() {//若函数从【调用1】处进入,则有: 1122 cout << "normal method begin" << endl; //输出,没问题 1123 //cout << _id << endl; //出错,因为这里等价于 this->_id,而this指针为NULL 1124 } 1125 }; 1126 1127 string CWithConstStatic::_sstr; //静态变量在类外的CPP中声明 1128 void NormalMethod(CWithConstStatic* _this) { 1129 1130 } 1131 1132 void TestCWithStatic() { 1133 1134 //1,常对象 1135 const CWithConstStatic ow; 1136 //ow._id = 1001; //error, 常对象不能被修改 1137 //ow._str = "dd"; //error, 常对象不能被修改 1138 ow._sstr = "dd"; //ok, 静态变量不属于对象 1139 1140 //2,常引用 1141 const CWithConstStatic& owRef = ow; 1142 //owRef._str = "hhh"; //error, 常引用不能被修改对象 1143 owRef._sstr = "dd"; //ok, 静态变量不属于对象 1144 1145 //3,常量指针,指向常量的指针,指向的内容不可修改 1146 const CWithConstStatic* pcwcs = new CWithConstStatic(); 1147 //pcwcs->_id = 20; //error,不可通过常指针更改其指向的内容 1148 1149 //4,指针常量,指针是一个常量,不可被再次赋值 1150 CWithConstStatic* const cpcwcs = new CWithConstStatic(); 1151 cpcwcs->_id = 20; //ok 1152 1153 //5,类函数原理,this指针 1154 //c++类的成员函数被编译器翻译为了C语言编译器可以识别的全局函数,然后用C语言编译器来处理它 1155 1156 //以下两条调用等价 1157 CWithConstStatic* pwcs = NULL; 1158 pwcs->NormalMethod(); //【调用1】C++的样子 1159 NormalMethod(pwcs); //【调用2】C语言翻译出来的结果 1160 1161 } 1162 #pragma endregion 1163 #pragma region 线程 1164 void ThreadFunc() { 1165 cout << "thread func 1 : " << _threadid<< endl; 1166 //Sleep(1000); 1167 } 1168 void TestThread() { 1169 std::thread t(ThreadFunc); 1170 cout << _threadid << endl; 1171 t.join(); 1172 } 1173 #pragma endregion 1174 #pragma region 深入模板 1175 #pragma region 可变参数模板 1176 void TestVarTemp() {//【无参的重载函数】 1177 //这个函数必须定义,否则编译器报错,因为函数参数展开时,最终(可变参数个数为0时)要调用此函数 1178 } 1179 1180 template<typename First, 1181 typename ... Args 1182 > 1183 void TestVarTemp(First first, Args... args) { 1184 //sizeof...是可变参数模板专用的获取参数个数的函数 1185 cout << sizeof...(args) << "-" << first << " "; 1186 1187 //可变参数展开的唯一方式是递归调用,一层层剥离参数,当参数个数为0时调用无参的重载函数,见【无参的重载函数】 1188 TestVarTemp(args...); 1189 } 1190 void TestVarTemplate() { 1191 TestVarTemp(1, 2, 3, 4); 1192 } 1193 #pragma endregion 1194 #pragma endregion 1195 #pragma region 构造和拷贝构造 1196 class CNormclass { 1197 public: 1198 CNormclass() { 1199 cout << "constructor" << endl; 1200 } 1201 CNormclass(const CNormclass& rhs) {//有了复制构造函数后,系统不再为类生成无参构造函数 1202 cout << "copy-constructor" << endl; 1203 *this = rhs; 1204 } 1205 }; 1206 1207 CNormclass TestConstructorAndCopyCon1() { 1208 return CNormclass();//不调用COPY构造函数 1209 } 1210 CNormclass TestConstructorAndCopyCon2() { 1211 //对象定义:两种不同的定义方式 1212 //方式一,会调用两次构造函数 1213 CNormclass r0; //constructor 1214 r0 = CNormclass(); //constructor,注意不是COPY构造函数 1215 1216 //方式二,只调用一次构造函数 1217 CNormclass rr = CNormclass(); //constructor 1218 1219 //COPY构造函数仅在两种情况下调用: 1220 //1,将一个已存在的对象生成另外一个对象 1221 CNormclass r1 = r0; //拷贝构造 1222 //2,将一个已存在的对象作为参数传递给构造函数时 1223 CNormclass r2(r0); //拷贝构造 1224 1225 //不调用构造函数,也不调用拷贝构造函数,也不调用=运算符(因为是同类型),只是进行按位copy 1226 r1 = r0; 1227 1228 cout << "before return " << endl; 1229 return rr; //调用COPY构造函数 1230 } 1231 #pragma endregion 1232 #pragma region 函数指针复杂嵌套 1233 typedef int(*PF2)(int); 1234 typedef PF2(*PF1)(int, int); 1235 typedef PF1(*PF)(int); 1236 int func2(int) { 1237 cout << "func2" << endl; 1238 return 0; 1239 } 1240 PF2 func1(int, int) { 1241 cout << "func1" << endl; 1242 return func2; 1243 } 1244 PF1 funcx(int) { 1245 cout << "funcx" << endl; 1246 return func1; 1247 } 1248 1249 void TestNestingFuncPtrs() { 1250 //1,一次嵌套 1251 PF1 pf1 = func1; 1252 pf1(1, 2)(1); 1253 1254 //等价方式的直接声明 1255 int(*(*ptr)(int, int))(int) = func1; 1256 ptr(2, 3)(4); 1257 1258 cout << "--------------------" << endl; 1259 1260 //2,二次嵌套 1261 PF pf = funcx; 1262 pf(1)(2, 3)(2); 1263 1264 //等价方式的直接声明 1265 int(*((*((*ptr2)(int)))(int, int)))(int) = funcx; 1266 ptr2(1)(2, 3)(2); 1267 } 1268 #pragma endregion 1269 #pragma region 类型转换构造函数 1270 class CTypeCast { 1271 public: 1272 int _id; 1273 string _name; 1274 CTypeCast(int i) {//整形转换构造函数:将一个整形转为对象 1275 _id = i; 1276 cout << "integer cast " << i << endl; 1277 } 1278 CTypeCast(string str) {//字符串转换构造函数:将一个字符串转为对象 1279 _name = str; 1280 } 1281 1282 //注意,显示声明,转换必须显式进行 1283 explicit CTypeCast(float fx) {//浮点转换构造函数:将一个字符串转为对象 1284 cout << "float cast " << fx << endl; 1285 } 1286 }; 1287 1288 void TestTypecastContructor() { 1289 //CTypeCast otc = 1; //整形转换构造函数 1290 //CTypeCast otc2 = "otc2"; //字符串转换构造函数 1291 //otc = 3; 1292 1293 //注意,当加了explicit后,类型转换必须显示进行,因此下面这个语句不会使用浮点转换构造函数 1294 //但是,它却可以使用整形转换构造函数,这会造成数据精度丢失 1295 CTypeCast otc3 = 3.2f; //隐式转换:整形转换构造函数 1296 CTypeCast otc4(3.2f); //显示转换:浮点转换构造函数 1297 1298 } 1299 #pragma endregion 1300 1301 #pragma region 2018.7.24 1302 #pragma region 类型转换运算符及()[]重载 1303 class CTypeCastOper{ 1304 float fx = 0.2f; 1305 int arr[3]{ 1,2,3 }; 1306 public: 1307 //1,类型转换运算符 1308 explicit operator float() { 1309 return fx; 1310 } 1311 operator string() { 1312 } 1313 1314 //2,()重载 1315 //()运算符并不是用来做类型转换的,它是当函数用的,即仿函数,或函数对象 1316 bool operator()() { 1317 return true; 1318 } 1319 1320 //3,[]重载 1321 //[]运算符与()差多的用法,都是用于对象之后 1322 int operator[](int idx) { 1323 return arr[idx]; 1324 } 1325 }; 1326 1327 void TestTypecastOper() { 1328 CTypeCastOper oper; 1329 float fx = (float)oper; 1330 cout << fx << endl; 1331 1332 //1,()运算符 1333 bool b = oper(); 1334 //2,[]运算符 1335 cout << oper[0] << "," << oper[1] <<"," << oper[2] << endl; 1336 } 1337 #pragma endregion 1338 #pragma region 模板特化 1339 template<typename T> 1340 class CTehuaTemp { 1341 public: 1342 T px = "abc";//2,被特化为了一个char*类型指针,故可以这样用 1343 }; 1344 template<typename T> 1345 class CDThhuaTemp : public CTehuaTemp<T*> {//1,将基类模板参数特化为一个指针类型 1346 public: 1347 T ch = ‘c‘; 1348 }; 1349 1350 void TestTehuaTemp() { 1351 CDThhuaTemp<char> otp; 1352 cout << otp.px << endl; 1353 cout << otp.ch << endl; 1354 } 1355 #pragma endregion 1356 #pragma region 同类型赋值,常引用修改 1357 class CSimpleclass { 1358 public: 1359 CSimpleclass() { 1360 cout << "cons" << endl; 1361 } 1362 1363 CSimpleclass(const CSimpleclass& rhs) { 1364 cout << "copy cons" << endl; 1365 } 1366 public: 1367 float fx = 0; //默认未初始化,给它来个初始化 1368 }; 1369 void TestSameTypeAssign() { 1370 1371 CSimpleclass oc, oc1; 1372 const CSimpleclass& oc2 = oc; 1373 const CSimpleclass& oc3 = oc; 1374 1375 cout << "-------------------------" << endl; 1376 //【同类型赋值,不调用=运算符,也不调用任何构造函数】 1377 oc1 = oc; 1378 1379 //oc2 = oc3; //常引用本身是个常量,也不能被修改 1380 //oc2 = oc1; //常引用本身是个常量,也不能被修改 1381 //oc2.fx = 30; //常引用不能更改引用的对象内容 1382 1383 const std::string ss; 1384 //ss = "abc"; //wrong 1385 //ss.clear(); //wrong 1386 } 1387 #pragma endregion 1388 #pragma region 堆指针栈指针判断 1389 class CTestPointerType { 1390 public: 1391 CTestPointerType(float fx=0) { 1392 this->fx = fx; 1393 } 1394 float fx; 1395 }; 1396 1397 template<class T, int N> 1398 class CHeapDebugger { 1399 public: 1400 static void Print(const T* p){ 1401 int sz = N * sizeof(T); 1402 1403 int* ip = (int*)p; 1404 int headFlag = *(ip - 1); 1405 int endFlag = *(int*)((char*)ip + sz); 1406 int orderFlag = *(ip - 2); 1407 int szFlag = *(ip - 3); 1408 1409 bool isHeapPtr = headFlag == endFlag && headFlag == 0xfdfdfdfd && sz == szFlag; 1410 cout << "----------------------------------------------" << endl; 1411 if (isHeapPtr) { 1412 cout << hex << "堆大小:" << szFlag << endl; 1413 cout << "堆编号: " << orderFlag << endl; 1414 cout << "堆首界: " << headFlag << endl; 1415 cout << "堆尾界: " << endFlag << endl; 1416 } 1417 else { 1418 cout << "栈指针" << endl; 1419 } 1420 cout << "----------------------------------------------" << endl; 1421 1422 } 1423 }; 1424 void TestPointerType() { 1425 // 1426 const int N = 4; 1427 int*p = new int[N]; 1428 for (int i = 0; i < N; i++) 1429 { 1430 p[i] = i; 1431 } 1432 1433 CNormclass* pn = new CNormclass[N]; 1434 CTestPointerType*po = new CTestPointerType[N]; 1435 1436 const int*pc = &N; 1437 CHeapDebugger<CNormclass, N>::Print(pn); 1438 1439 delete po; 1440 1441 } 1442 #pragma endregion 1443 #pragma endregion 1444 1445 #pragma region 右值引用和MOVE 1446 void TestRef(){ 1447 int a = 0, b = 1; 1448 int& ra = a; 1449 cout << ra << endl; //0 1450 ra = b; //此时ra不是a的引用也不是b的引用,而是一个普通变量 1451 b = 300; 1452 cout << ra << endl; //1 1453 1454 1455 } 1456 #pragma endregion 1457 #pragma region C11智能指针 1458 1459 #pragma endregion 1460 #pragma region 正则表达式 1461 1462 #pragma endregion 1463 #pragma region lambda表达式 1464 1465 #pragma endregion 1466 #pragma region unorder_map及hashtable实现 1467 //有没有无冲突哈希算法 1468 1469 #pragma endregion 1470 #pragma region DIJKASTRA最短路径算法 1471 1472 class Obj { 1473 public: 1474 Obj(float fx) { 1475 x = fx; 1476 } 1477 float x; 1478 }; 1479 bool cmpfunc(Obj a, Obj b) { 1480 return a.x < b.x; 1481 } 1482 1483 void TestStlSortFunc() { 1484 std::vector<Obj> vec; 1485 vec.push_back(Obj(1)); 1486 vec.push_back(Obj(12)); 1487 vec.push_back(Obj(1.3f)); 1488 vec.push_back(Obj(2.31)); 1489 vec.push_back(Obj(31)); 1490 vec.push_back(Obj(4)); 1491 vec.push_back(Obj(0)); 1492 1493 int ax = 123; 1494 auto iter = max_element(vec.begin(), vec.end(), [ax](Obj obj1, Obj obj2){ 1495 cout << "cap addr of ax : " << ax << endl; 1496 return obj1.x < obj2.x; 1497 }); 1498 cout << (*iter).x << endl; 1499 } 1500 1501 void RemoveVecElem(std::vector<int>& v, int e) { 1502 for (auto it = v.begin(); it != v.end();) { 1503 if (*it == e) 1504 { 1505 it = v.erase(it); 1506 break; 1507 } 1508 else 1509 it++; 1510 } 1511 } 1512 void Dijkastra() { 1513 const int m = 99999; 1514 const int n = m; 1515 const int nodeCount = 7; 1516 1517 int paths[][nodeCount] = { 1518 { n, 50, 12, m, 45, m, m }, 1519 { m, n, m, m, 2 , m, m }, 1520 { m, 10, n, 99, m , m, m }, 1521 { m, m, m, n, m , m, m }, 1522 { m, m, m, 10, n , m, m }, 1523 { m, m, m, m, 0 , n, 1 }, 1524 { m, 1, m, m, m , m, n }, 1525 }; 1526 1527 std::vector<string> sel; 1528 std::vector<int> left{ 0, 1, 2, 23, 4, 15, 6 }; 1529 sel.reserve(8); 1530 left.reserve(8); 1531 1532 int startIdx; 1533 cout << ">> 选择一个起点 " << endl; 1534 cin >> startIdx; 1535 cout << ">> v" << startIdx << endl; 1536 1537 if (startIdx >= nodeCount) 1538 return; 1539 1540 RemoveVecElem(left, startIdx); 1541 cout << "after erase : " << left.capacity() << endl; 1542 for (auto e:left) 1543 { 1544 cout << e << ","; 1545 } 1546 cout << endl; 1547 1548 cout << ">> calculating ..." << endl; 1549 int tmp[nodeCount]; 1550 for (int i = 0; i < nodeCount; ++i) { 1551 tmp[i] = paths[startIdx][i]; 1552 } 1553 1554 1555 std::stringstream ss; 1556 //ss >> "v" >> startIdx; 1557 1558 auto iter = min_element(tmp, tmp + nodeCount); 1559 cout << *iter << "," << iter - tmp << endl; 1560 1561 int curMinNode = iter - tmp; 1562 int curMinPathLen = *iter; 1563 // ss >> "->v" >> curMinNode; 1564 //sel.push_back(ss.str()); 1565 //ss.clear(); 1566 RemoveVecElem(left, curMinNode); 1567 1568 while (left.size() > 0) { 1569 bool isfind = false; 1570 for (int i = 0; i < nodeCount; ++i) { 1571 int p1 = paths[startIdx][i]; 1572 for (int j = 0; j < nodeCount; ++j) { 1573 bool isold = false; 1574 for (int i = 0; i < left.size(); ++i) { 1575 if (left[i] == j) 1576 isold = true; 1577 } 1578 if (!isold) { 1579 int p2 = paths[curMinNode][j]; 1580 if (j != curMinNode) { //j != curMinNode 1581 if ((curMinPathLen + p2) < p1) { 1582 isfind = true; 1583 paths[startIdx][i] = (curMinPathLen + p2); 1584 } 1585 } 1586 } 1587 } 1588 } 1589 1590 if (left.size() == 0)break; 1591 1592 auto p = paths[startIdx]; 1593 auto iter2 = std::min_element(left.begin(), left.end()); 1594 curMinPathLen = *iter2; 1595 //curMinNode = iter2 - left.be; 1596 RemoveVecElem(left, curMinNode); 1597 cout << "left: " << left.size() << endl; 1598 } 1599 1600 // sel.push_back(0); 1601 // sel.erase(sel.begin()); 1602 // sel.shrink_to_fit(); 1603 // cout << "cap: " << sel.capacity() << endl; 1604 // for (int d : sel) 1605 // { 1606 // cout << d << endl; 1607 // } 1608 // cout << sel.size() << endl; 1609 } 1610 #pragma endregion 1611 #pragma region EffectiveC++ 1612 namespace EffectiveCpp { 1613 #pragma endregion x 1614 #pragma region 02-以const,enum,inline替代define 1615 class CStaticConst { 1616 public: 1617 //【1】,static const 可以同时存在,这在C#中是不允许的 1618 //在C#中,常量也是属于类而不属于对象,这就等价于C++的 static cosnt 合体了 1619 static const float fx; //【声明式】 1620 1621 //【2】,浮点类型,不能在定义时初始化 1622 //static float fx2 = 3; //【错误】 1623 1624 //【3】,整数类型(整形,char,枚举),可以在定义时初始化,且不需要在类外写定义式 1625 static const int ix = 3; //声明并初始化,注意,这不是定义,也就是说声明时可以赋值 1626 1627 enum {NumTurns = 5}; 1628 int scores[NumTurns]; //enum hack 1629 1630 //【不安全宏的替代品】,既有宏的高效率和函数的安全性 1631 template<typename T> 1632 inline T safe_max(const T& a, const T& b) { 1633 return a > b ? a : b; 1634 } 1635 }; 1636 const float CStaticConst::fx = 1; //【定义式】:不能写static 1637 //const int CStaticConst::ix = 3; //【错误】,已经初始化过了,不能重复 1638 const int CStaticConst::ix; //定义式,声明时已初始化了。因为是整数类型,这个定义式可以不写 1639 1640 //1,【宏是不安全的】任何时候都不要忘了给宏的实参加上() 1641 //2 替代方法:使用 template inline 1642 #define unsave_max(a, b) (a) > (b) ? (a) : (b) 1643 1644 void Test02() { 1645 CStaticConst oc; 1646 cout << oc.fx << endl; 1647 int a(10), b(20); 1648 1649 //不安全的宏,下面这样的导致b被加两次 1650 max(++a, b++); 1651 cout << "a=" << a << ", b=" << b << endl; 1652 } 1653 #pragma endregion 1654 1655 1656 } 1657 1658 #pragma endregion 1659 1660 #pragma endregion 1661 const int* TestConstarr() { 1662 int* iarr = new int[3]{ 1, 2, 3 }; 1663 return iarr; 1664 } 1665 int _tmain(int argc, _TCHAR* argv[]) 1666 { 1667 EffectiveCpp::Test02(); 1668 //TestStlSortFunc(); 1669 //Dijkastra(); 1670 //TestPointerType(); 1671 //TestSameTypeAssign(); 1672 //TestRef(); 1673 //TestTehuaTemp(); 1674 //TestCComplexOper(); 1675 //TestTypecastOper(); 1676 //TestTypecastContructor(); 1677 //TestNestingFuncPtrs(); 1678 //TestArrayAndPointer(); 1679 ///TestRealloc(); 1680 //TestComputeDataStorage(); 1681 //TestVirtualFunctionTable(); 1682 //TestAdd2Num(); 1683 //TestAstrPstr(); 1684 //TestCWithStatic(); 1685 //TestThread(); 1686 //TestVarTemplate(); 1687 1688 const int arr[] = { 1, 23, 4 }; 1689 int a1[3], a2[3]; 1690 TestConstarr(); 1691 return 0; 1692 }
以上是关于C++全总结的主要内容,如果未能解决你的问题,请参考以下文章
Pytorch优化器全总结牛顿法BFGSL-BFGS 含代码
Pytorch优化器全总结AdadeltaRMSpropAdamAdamaxAdamWNAdamSparseAdam(重置版)