C++全总结
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++中字符串有常量和變量之分,字符串遇到\0則結束 266 ////C#中只有常量字符串,字符串遇到\0不結束,視其為正常字符 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() != '\n'); //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 }?
posted on 2018-07-30 17:00 時空觀察者9號 閱讀(...) 評論(...) 編輯 收藏
總結
- 上一篇: C# 全总结
- 下一篇: 【转】C++11多线程的基本使用