2018.8.14-C++複習筆記總

// CPPTEST.cpp : 定義控制檯應用程序的入口點。 //  #include "stdafx.h" #include<iostream> #include <map> #include<fstream> #include<cassert> #include <sstream> #include"TMyNumOperator.h" #include"abc.h" #include <list> #include<thread> #include <vector> #include <algorithm> #include <cmath> #include <chrono> #include <mutex>

using namespace std; using std::cin; using std::cout; //using namespace std; //
//class CBase{ //protected://注意,可使用C#風格的定義時初始化 // std::string name = "NoOne"; // int age = -20; // int sex = 1; //public: // float *pData = new float[20]{1, 2, 3, 4, 5}; //public: // virtual ~CBase(){//虛析構函數,防止內存泄漏:對基類指針調用delete時,會從子類一直析構到基類 // cout << "~cbase" << endl; // } //}; // ////基類的私有成員不會被繼承,這和C#徹底同樣
//class CStudent : public CBase{ //public: // std::map<int, std::string> _projs; // CStudent(){ // pData = new float[20]{1, 2, 3, 4, 5}; // } //public: // void SetName(const std::string& name){ // CBase::name = name;//若是CBase.name定義爲私有,這裏就不可訪問 // this->name = name; //等價於上一行 // } //
// const string& GetName(){ // return this->name; // } //
// ~CStudent(){//若採用淺拷貝,析構函數被調用屢次,pData被刪除屢次,程序崩潰 //        //規避方式:判斷pData是否爲空,非空才delete[] pData //        //但在不知情的狀況下使用pData仍然會出問題,所以淺拷貝致使的問題不可規避 // cout << "~cstudent" << endl; // delete[] pData; // pData = NULL; // } //}; //
//void TestSTL(){ //    
// auto mp = new std::map<int, std::string>();//c++11新風格 auto //    
// mp->insert({ 10, ("h你好") });//c++11新風格,不用再使用std::pair()或std::make_pair() // mp->insert({ 20, "el" }); // for (auto var : *mp)//c++11新風格for // { // std::cout << var.first << "," << var.second << "," << std::endl; // } //} //
//void TestClass(){ // CBase* pbase = new CStudent(); // auto pst = (CStudent*)pbase; // pst->SetName("xxxx"); // auto name = pst->GetName(); // delete pbase; //} //
//int TestAdd(int a, int b){ // return a + b; //} //void TestStdFunc(std::function<int(int,int)> fun, int a, int b){ // auto ret = fun(a, b); //} //
//typedef int(*TestAddPtr)(int, int); //
//void TestPCall(TestAddPtr func, int a, int b){ // auto ret = func(a, b); //} //
//struct Vertex{ // bool isgood; // float x, y, z; // double dx; // bool bx; // int ix; // bool by; //}; //void TestFile(){ // int szChar = sizeof(char); // ofstream ofs; // ofs.open("f:/test.txt"); // ofs << "hello " << 10 << " world " << 20 << endl; //
// ofs.flush(); // ofs.close(); //    
// ifstream ifs; // ifs.open("f:/test.txt"); // string str1, str2; // int num1, num2; //
// ifs >> str1 >> num1 >> str2 >> num2; //
//    //錯誤示例:二進制讀寫,使用std::<<或>>進行的仍是ASCII碼的讀寫 // ofstream ofsb; // ofsb.open("f:/testb", ios::binary); // ofsb << "hellob" << 1022; //
// ofsb.flush(); // ofsb.close(); // ifstream ifsb; //
// string sx; // int nx; // ifsb.open("f:/testb", ios::binary); // ifsb >> sx >> nx; // ifsb.close(); //
//    //正確作法 // sx = "binary"; // nx = 978; // ofsb.open("f:/testbx", ios::binary); // ofsb.write(sx.c_str(), sx.length()*sizeof(char)+1); // ofsb.write((const char*)&(nx), sizeof(int)); // ofsb.flush(); // ofsb.close(); //
// char sxr[32]; // int nxr; // ifsb.open("f:///testbx", ios::binary);//注意這裏的"///"無論有多少個/都等同於一個 // ifsb.read(sxr, sx.length()+1); // ifsb.read((char*)&nxr, 4); //
//    //數據轉換的更通用方式 // Vertex vt; // vt.bx = true; // vt.isgood = false; // vt.x = 12; // vt.y = 13; // vt.z = 14; // vt.dx = 3.9; // vt.by = 0; //
// ofstream ofsbx; // ofsbx.clear(); // ofsbx.open("f:/testbyx2", ios::binary); // ofsbx.write((const char*)&(vt), sizeof(Vertex)); // ofsbx.flush(); // ofsbx.close(); //
// ifstream ifsbx; // Vertex vrt; // ifsbx.clear(); // ifsbx.open("f:/testbyx2", ios::binary); // ifsbx.read((char*)&vrt, sizeof(Vertex)); //
// string s1 = "hello"; // string s2 = "wrold"; // s1 = s1 + s2; // auto s3 = s1.substr(1, 2); //} // ////實現較爲高效的字符串分割,限制是分割符只能是一個字符,不能是一個串
//std::list<string> TestStrSplit(string s, char sep){ // std::list<string> lst; // for (int i = 0, j = 0; i < s.length(); ++i){ // if (s[i] == sep){ // lst.push_back(s.substr(j, i - j)); // j = i + 1; // } // } //
//    //注意臨時對象做爲返回值了,通常狀況下這是錯誤的用法,棧上的臨時對象出了函數域後會被釋放 //    //但這裏STL容器內部重載了=運算符,做了值拷貝就沒問題了 // return lst; //} //void TestString(){ //
//    //g正則表達式實現字符串分割 // string s1 = "a;b;c;dddd;ef;"; // string s2 = "a123b2673cdd4444a"; // std::regex re("(\d+)"); // std::smatch mtch; //
//    //這個作法效率挺低且浪費內存,產生了不少中間字符串 // while (std::regex_search(s2, mtch, re, std::regex_constants::match_default)){ // cout << mtch.str() << endl; // s2 = mtch.suffix(); // } //
//    //這個函數效率要高多了 // auto lst = TestStrSplit(s1, ';'); //    
//} // ////返回棧上的臨時對象測試
//CStudent TestTempObjRet(){ // CStudent ost; //臨時對象 // return ost; //調用對象的拷貝構造函數 //}//出了棧後ost被釋放,析構函數調用,同時成員對象被析構CStudent.name="",但內置類型仍保持原值 // ////經過測試可知,將棧上對象做爲函數返回值使用通常是沒有問題的,但淺COPY時兩個對象中的指針指向同一份 ////內存,當一個對象被刪除時,另外一個對象中的指針就指向非法位置了,成了野指針
//void TestObjConstructorAndDestructor(){ // CStudent ostx; // ostx = TestTempObjRet(); //調用拷貝構造函數(與上面對應) // auto name = ostx.GetName(); // auto px = ostx.pData; //} //
//void TestRRef(){ //
//} // ////可使用隨機訪問(數組下標)說明vector在內存中是連續存放的 ////這樣,vector在須要擴充容量時就須要將原來內存刪除,再申請一塊新內存 ////但這並不必定,由於內存申請時若用realloc則有可能會在原內存後面增長(原理)
//void TestVector(){ // std::vector<string> sv{ "hello", "world" }; // sv[0]; // sv[1]; //    
// sv.reserve(20); //舊的內容被清除 // int n = sv.capacity(); //20 // sv.push_back("a"); // sv.push_back("b"); // sv.clear(); //舊的內容被清除 // n = sv.capacity(); //20 //
// sv.shrink_to_fit(); //內存釋放 // n = sv.capacity(); //0 //
//} //
//struct CTA{ //private: // virtual void Test(){ // cout << "cta" << endl; // } //
//}; //
//class CCTA : CTA{//類和結構體能夠相互繼承 //public: // int _id; // void Test() const{ // cout << "ccta-test" << endl; // } //}; // ////C++中字符串有常量和變量之分,字符串遇到\0則結束 ////C#中只有常量字符串,字符串遇到\0不結束,視其爲正常字符
//void TestStr(){ // char* ps = "hello";//字符串常量,不可修改其內容 // ps[0] = 'd'; //運行出錯 //
// char arr[] = "hello"; //字符串變量 // char* par = arr; // arr[0] = 'd'; //ok //} // ////C++中指針字符串與數組字符串都是自動以0結尾的
//void TestMemcpy(){ //    
// char dest[18]; // char src[] = "hell"; //以0結尾,長度爲5,若強制聲明爲 char src[4] = "hell"則編譯報錯 // char* psrc = "hell"; //以0結尾,長度爲5,但測試長度strlen(psrc)爲4,由於它沒算尾0 //    
// for (int i = 0; i < 10; ++i){ //
// } // for (int i = 0, ch; (ch = psrc[i++]) != 0;){ //        //這裏發現字符串尾0後有許多個0,不知道緣由 //
// } // auto len = strlen(psrc); //4,測試長度,並沒字符串的真實長度(內存中真實串),由於它有尾0 // int len2 = strlen(src); //5,字符串實際長度(內存中存儲的字符串) // int st = sizeof(src); //5,數組大小 // memcpy(dest, psrc, strlen(psrc)+1); //} //template<typename T1, class T2> class MyVector{ // std::vector<int> _lst; //
//public: //
// void Test2(); //}; //
//template<class T1, class T2> void MyVector<T1, T2>::Test2(){ //
//}

#pragma region 2018.7.7 [module(name = "mytestx")]; void TestIOStream() { std::fstream fs; fs.open("test.txt", ios_base::in | ios_base::out); fs << 12 << "hello"; fs.seekp(0); int ix1; string sx1; char chs[6]; fs >> ix1; fs >> chs; chs[5] = 0; sx1 = chs; cout << ix1 << sx1.c_str() << endl; } void TestMacro() { #define hfunc(x) cout << x << endl; //自定義處起,全局可見 hfunc(124); #undef hfunc

    //typedf, using等價使用
    typedef void(*PFUN)(int); using PFUNC = void(*)(int); using Int = int; using MyType = Int; } //數組和指針
void TestArrayAndPointer() { //1,char* p : char類型指針,指向char數組, p++移動一個char //2,int* p : int型指針,指向int數組,p++移動一個int //3,char(*p)[2] : char[2]類型指針,指向char[2]類型數組,即char[][2]數組,p++移動一個char[2] //總結:X類型的指針指向X類型的數組, p++移動一個數組元素 //如何看指針類型:去除*p剩下的就是類型,如char*p去掉*p爲char,char(*p)[2]去掉*p爲char[2] //======================================================== //指針老是指向數組的,以下,可認爲是指向只有一個元素的數組 //========================================================
    int ix = 20; int*pix = &ix; cout << pix[0] << "," << *pix << endl; //======================================================================================== //堆和棧上數組的初始化列表寫法 //========================================================================================
    char arr[43] = { 'a','b','c' }; char arr2[10] = { "hello" }; int iarr[] = { 1, 2, 3, 4 }; char*ps = new char[30]{ 0 }; int* ips = new int[30]{}; int* ips2 = new int[30]; //cout << arr << "," << (void*)arr << (void*) ps << endl;
    char* px; px = arr; //能夠賦值,說明數組名與指針等價
    const char* cp;//能夠cp++;
    char* const cpx = arr; //不能夠 cpx++,不能移動的指針,數組名其實就是這種指針 //這裏以arr與ps做對比,數組名與指針本質上都是指針,只是數組名是不能移動,不能賦值的常指針 //在二維情形時也是如此
 stringstream ss; //======================================================================================== //1,棧上二維數組,【內存連續】 //========================================================================================
    char a[][3] = {//二維數組初始化列表
        { 98, 99, 100 }, { 101, 102, 103 }, }; for (int i = 0; i < 6; ++i) {//驗證
        ss << *(*a + i) << ","; } cout << ss.str() << endl; //============================================================================= //2,數組指針(也稱行指針),【內存連續】 //=============================================================================
    int(*pax)[4] = new int[3][4]; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { pax[i][j] = i * 4 + j + 1; } } ss.str(""); for (int i = 0; i < 12; ++i) {//驗證
        ss << *(*pax + i) << ","; } cout << ss.str() << endl; //============================================================================= //3,指針數組,【內存不連續】 //============================================================================= //由於它是一個數組,因此不能用new來給它分配內存,new出來的東西只能賦值給指針
    char* arr_p[2]; arr_p[0] = new char[30]{ 'h','e','o','l','l' }; arr_p[1] = new char[10]{ 'a','b','c' }; //============================================================================= //4,多級指針用來分配二維數組,有【連續內存分配法】和【不連續內存分配法】 //這個很是重要,若用一個不連續的二維數組指針進行memcpy操做,則會發生嚴重問題: //(1)數據拷越界,覆蓋了其它變量甚至程序的內存 //(2)dest變量中數據只填充了一部分,其他部分仍是舊數據,致使程序出現莫名其妙的問題 //(3)這種數據拷越界並沒有任何提示,隱蔽性極高,很是難以查找 //=============================================================================
    int**pi = new int*[3]; int* ptemp = new int[12]; for (auto i = 0; i < 3; ++i) { //------------------------------------------------ //(1)【不連續內存分配法】 //pi[i] = new int[2]; //------------------------------------------------ //(2)【連續內存分配法】
        pi[i] = &((ptemp + i * 2)[0]); for (int j = 0; j < 2; ++j) { pi[i][j] = i * 2 + j; } } for (int i = 0; i < 3; ++i) {//驗證
        for (int j = 0; j < 2; ++j) { ss << pi[i][j] << ","; } } cout << ss.str() << endl; } void TestInitialist() { class CIn { public: float x, y, z; string name; }; //初始化列表的使用條件: //無自定義構造函數,成員公有,無基類,無虛函數 //這麼多限制,能夠說很雞肋
    CIn oin = { 1, 2, 3, "hello" }; //方式1
    CIn oin2 { 1, 2 ,3, "world" };  //方式2
} #pragma endregion

#pragma region 2018.7.9
class CComplex { float real, image; public: CComplex(float real, float image) { cout << "constructor: " << real << "," << image << endl; this->real = real; this->image = image; } CComplex(const CComplex& other) { cout << "copy constructor: " << other.real << "," << other.image << endl; if (this != &other) { real = other.real; image = other.image; } } ~CComplex() { cout << "~ccomplex" << "(" << real <<"," <<image << ")" << endl; // real = 0; // image = 0;
 } void PrintInfo() { cout <<"Complex: " <<  real << "," << image<< endl; } public: //------------------------------------------- //運算符重載 //------------------------------------------- //1,重載爲成員函數
    CComplex operator+(const CComplex& other) { cout << "operator+" << endl; return CComplex(real+other.real, image + other.image); } CComplex& operator++() {//前向++
        cout << "forward ++ " << endl; real++; image++; return *this; } CComplex& operator++(int) {//後向++
        cout << "backward ++ " << endl; real++; image++; return *this; } const CComplex& operator=(const CComplex& other) { this->real = other.real; this->image = other.image; return *this; } //2,重載爲友元函數
    friend CComplex operator+(float fx, const CComplex& cp); //3,【運算符重載函數不能定義爲靜態函數】 //這與C#不一樣,C#中全部運算符重載都必須是public和static的 //static CComplex operator+(float fx, const CComplex& cp); //4,類型轉換運算符重載
    operator bool() {//使用情景:CComplex oc; if(oc){}或if(oc != NULL){}或 float/int/bool x = oc
        return real != 0 && image != 0; } operator float() {//使用情景:CComplex oc; if(oc){}或if(oc != NULL){}或 float/int/bool x = oc
        return real; } // CComplex operator=(const CComplex& other) { // if (this == &other) // return other; // return CComplex(other.real, other.image); // }

    void Testx() { CComplex* pNewCom = new CComplex(2, 2); pNewCom->real = 20;//能夠訪問私有成員??
 } }; // CComplex CComplex::operator+(float fx, const CComplex& cp) { // return CComplex(fx + cp.real, cp.image); // }
CComplex operator+(float fx, const CComplex& cp) { return CComplex(fx + cp.real, cp.image); } void TestCComplexOper() { int i = 10; CComplex cpx(1, 2); ++cpx++++; cpx.PrintInfo(); } CComplex TestReturnStackObj() { //----------------------------------------------------------------- //返回棧上的對象 stackObj //返回棧上的對象會致使拷貝構造函數的調用,生成一個
    CComplex stackObj(1, 2); return stackObj; return CComplex(1, 2); //這種方式直接調用構造函數,而不調用拷貝構造函數 //-----------------------------------------------------------------
} #pragma endregion

#pragma region 2018.7.10
void TestRealloc() { cout << "---------------test-realloc---------------" << endl; int szch = sizeof(char); char*pstr = "this is a test str"; int strLen = strlen(pstr); char* pdesc = (char*) malloc((1+strLen)* sizeof(char)); for (int i = 0; i < strLen; ++i) { cout << "," << hex<< (int)pdesc[i]; } cout << endl; cout << strlen(pstr) << endl; strcpy_s(pdesc, strLen+1, pstr); for (int i = 0; i < strLen; ++i) { if(pdesc[i] > 0) cout << (char)pdesc[i]; else cout << "," << (int)pdesc[i] ; } cout << endl; pdesc = (char*)realloc(pdesc, 40); for (int i = 0; i < 40; ++i) { pdesc[strLen + i] = 'a' + i; } for (int i = 0; i < 40 + strLen; ++i) { if (i < strLen) cout << pdesc[i] << ","; else cout << (unsigned short)pdesc[i] << ","; } cout << endl; cout << "---------------test-realloc---------------" << endl; } template<typename T> class CMyNumOperator { T a, b; public: static T Add(T x, T y) { return x + y; } }; #pragma endregion

#pragma region 2018.7.11 
#pragma region 繼承相關
class A { public: A(int x) { fProtected = x; } float GetFProtected() { return fProtected; } public: float fpublic = 2.3f; //c++11支持了初始化,但不能使用auto
    string sname = "liqi"; CMyNumOperator<int>* on = new CMyNumOperator<int>(); //對象也能夠

    void TestFunc() { cout << "TestFunc " << fProtected << endl; } static void StaticTestFunc() { cout << "Static-TestFunc" << endl; } virtual void ToString() { cout << "A::ToString" << endl; } protected: float fProtected; void ProtectedFunc() { cout << "PRotectedFunc" << endl; } private: void PrivateFunc() { cout << "PrivateFunc" << endl; } }; //只管公有繼承,無論保護繼承和私有繼承,意義不大,也太複雜 //C++能夠直接調用構造函數嗎? //能夠,有兩種 //1,構造函數列表中 //2,new T()構造對象時

class B : public A { public: friend void TestProtectedDerive(); B() :A(1) {} //顯示調用構造函數【1】
    void TestForDerive() { //公有繼承下 //1,子類能夠訪問父類的保護成員,不能訪問父類的私有成員
 B ob; //PrivateFunc(); //error,子類不能訪問基類的私有成員
        ProtectedFunc(); //right
        fProtected = 10; //right
        ob.fProtected = 20; //right
        A* pa = new A(1); //顯示調用構造函數【2】
 } //1,c++中只要基類有相同簽名虛函數,則默認爲此基類函數也是虛函數[與C#不一樣],以下情形都成立 // (1) 函數不聲明 virtual // (2) 函數聲明瞭 virtual // (3) 函數聲明瞭 override // (4) 函數聲明瞭 virtual 和 override //2,c++中兩個關鍵詞做用不一樣,能夠同時存在 // virtual僅代表函數是虛函數,override是C++11中出現的,明確說明是對基類的重寫 // 它的好處是當函數聲明不符合規則時,編譯器會報錯
    void virtual ToString() override{ cout << "B::ToString" << endl; } }; void TestProtectedDerive() { B ob; ob.ProtectedFunc(); } #pragma endregion
#pragma endregion
#pragma region 2018.7.18
#pragma region 標準輸入流
void TestCinCout() { float fx; std::string str; while (true) { bool errorNum = false; cin >> str; //1,試讀,看是否是"exit"串
        if (str == "exit")//2,如果,結束循環
            break; for (int i = str.length() - 1; i >= 0; --i) {//3,若不是,將串放回到流中,注意是反向放回的
 cin.putback(str[i]); } cin >> fx; if (cin.fail()) {//4,若是格式錯誤
            cout << "格式錯誤:請輸入一個數值" << endl; cin.clear(); //5,清除錯誤標識
            while (cin.get() != '\n'); //6,讀掉後面出錯的全部字符,直到回車
            errorNum = true; } if (!errorNum) {//7,若前面輸入(數字)是正確的,則繼續後面的解析
            cin >> str; if (cin.fail()) { cout << "格式錯誤:請輸入一個字符串" << endl; cin.clear(); } cout << ">>數值= " << fx << ", 描述= " << str << endl; } } } #pragma endregion
#pragma region 計算機數據存儲
void TestComputeDataStorage() { //數據轉換:C++,C# 通用 //1,整形數據:短數據類型轉長數據類型時,正數高位補0,負數高位補1 //2,浮點形數據轉整形時,獲得整數部分,捨去了小數部分
 cout << hex; cout << (int)(short)1 << endl; //1,即 0x00000001
    cout << (int)(short)-1 << endl; //0xffffffff,即負數高位補1
    cout << -1 << endl; //0xffffffff,負數表示法,符號位1,真值(1)求補碼
 auto sz = sizeof(long);//64位系統,X64編譯器下VS2017測試值爲4
    float fx = 83.7f; auto lfx = (long unsigned int)fx; //浮點轉整形,
    long long x; //8位整形
    long unsigned int lui; //8位無符號整形 //浮點數據字節察看 //125.5f = 0x42fb0000 //-125.5f = 0xc2fb0000 //83.7f = 0x42a76666 //浮點數存儲按IEEE754標準: //以float爲例:共4個字節,從高位到低位依次是31,30,...2,1,0 //最高位存放數據符號,接下來8位存放階碼(包括階碼符號位),接下來23位存放尾數
    int ifx = *(int*)(&fx); //等價於
    int* pfx = (int*)&fx; int ipfx = *pfx; int sz2 = sizeof(x); } #pragma endregion
#pragma region 地址與指針
void TestAddrAndPointer() { //------------------------------------------------------------- //1,&p, p, *p的區別: &p是p的地址,p是一個地址,*p是地址中的內容 //2,地址與指針徹底等價,有兩種操做:*地址,地址-> //3,地址就是一個數值,指針也是個地址
    int x = 10; *(&x) = 0x100; *((char*)&x) = 1;        //小端模式下[低字節存低地址處,高字節存高地址處]:0x101
    int* pxt = (int*)10;    //直接指向內存地址0x0000000a處
    int*px = &x;            //px與 &x徹底等價
    int adr = (int)(&x);    //地址就是個數值,指針也是個地址值
    px = (int*)adr; cout << hex; //輸出爲16進制
    cout << adr << "," << &x << "," << (int*)&x << "," << px << endl; //四者等價,輸出相同值
    cout << dec; //輸出爲10進制
 A oa(0); (&oa)->fpublic = 30;    //地址與指針等價
    (*(&oa)).fpublic = 111;    //地址與指針等價
 } #pragma endregion
#pragma region 函數指針
void TestFuncPtr() { cout << "TestFuncPtr" << endl; } void TestFuncPtrParam(int, int, int) {//注意函數參數能夠不寫變量名
    void(*pf)(int, int, int) = TestFuncPtrParam; int*p = (int*)pf; //試圖找出函數實參,失敗,對函數彙編原理不清楚,有時間再查
    cout << *(p) << "," << *(p-1) << endl; } void TestFuncPointer() { A oa(0); //1,函數指針與普通指針不兼容,不能相互強轉 //2,函數指針賦值方式有二:pf = func或 pf = &func //3,函數指針pf使用方式有二:pf()或 (*pf)(),由於pf和 *pf的值相同,調試模式下能夠看到 //1,普通成員函數指針
    typedef void(A::* PFUNC)(void);        //函數指針聲明方式一
    using PFunc = void(A::*)(void);        //函數指針聲明方式二,C++11新方式
 PFunc pf = &(A::TestFunc); int pfsz = sizeof(pf); (oa.*pf)(); //2,全局函數指針
    void(*pfg)() = TestFuncPtr; pfg(); (*pfg)(); //3,靜態函數指針
    void(*sptf)() = A::StaticTestFunc; sptf(); (*sptf)(); } #pragma endregion
#pragma region 虛函數表原理
//每個帶有虛函數的【類】都有一個虛函數表,注意不是對象
void TestVirtualFunctionTable() { cout << hex; typedef void(*PFUNC)(); offsetof(A, fpublic); //利用此函數能夠算函數佈局
 A oa(0); B ob; //一,經過內存地址修改不可訪問的保護變量
    *(float*)((int*)&oa + 1) = 123.4f; //類的第一個變量fpublic賦值,(int*)&oa + 1是跳過虛函數指針
    float fpublic = oa.fpublic; //二,經過內存地址調用虛函數 //A和B的虛函數表地址不同,也就是說父類和子類各有一張虛函數表
    int* pvptr = (int*)(*((int*)(&oa))); cout << "A的虛函數表地址:" << pvptr << endl;    //000DB0D4
    ((void(*)())(*pvptr))();                    //A::ToString
 pvptr = (int*)(*((int*)(&ob))); cout << "B的虛函數表地址:" << pvptr << endl; //000DB128
    ((void(*)())(*pvptr))();                    //B::ToString
 cout << "--------------------------" << endl; //最簡寫法
    ((void(*)())(*((int*)*(int*)&oa)))(); ((void(*)())(*((int*)*(int*)&ob)))(); } #pragma endregion
#pragma region 函數對象,友元函數模板運算符重載 template<class T>
class AddTwoNumber { public: T x; AddTwoNumber(T x) { this->x = x; } public: //【經過重載()運算符,實現函數對象】
    T operator()(T a, T b) { return a + b; } //一,使用模板類型的友元模板函數 //1, <>表示該友元是一個模板函數,且使用本模板類的類型 // 若不加<>說明符,則找不到模板函數定義,運行時出錯 //2,這裏的T是模板類傳來的類型,所以,這裏不能實現與T不一樣的類型操做 //好比若T爲int,則 2.1f + new AddTwoNumber<int>()不合法 //3,【注意這裏第二個參數是個引用類型,如果AddTwoNumber<T>對象類型則會出錯,不能在類中定義本類對象】
    friend void operator+ <>(T os, AddTwoNumber<T>& n); //二,使用模板函數自帶類型的友元模板函數 //這裏的T是一個新的類型,與此模板類的T不要緊,所以沒有上面的限制
    template<class T> friend void operator+(T os, A oa); template<class T> T Add(T a, T b); }; template<class T>
void operator+ <>(T os, AddTwoNumber<T>& n) { cout << "operator+: n + AddTwoNumber: " << os << endl; } template<class T>
void operator+(T n, A o) { cout << "operator+: n + A : " << n << endl; } //================================================== //※※※※※※注意這種多層的模板前置聲明※※※※※※※
template<typename T> //類模板的前置聲明
template<typename T1> //函數模板的前置聲明
T1 AddTwoNumber<T>::Add(T1 a, T1 b) { return a + b; } void TestAdd2Num() { AddTwoNumber<double> NumAdd(1); auto nadd = NumAdd(1, 2); A oa(1); 2.1f + oa; //左操做數任意數值類型,由於使用的是模板函數自帶類型
    2.0 + NumAdd;//左操做數必須爲double,
 AddTwoNumber<string> add2("str"); add2.Add(1, 1); cout << "x: " << add2.x << endl; } #pragma endregion
#pragma endregion
#pragma region 2018.7.19
#pragma region 智能指針

//----------------------------------------------------------------------------------------------
 template<typename T>
class SmartPointerx { private: T * _ptr; size_t* _count; public: SmartPointerx(T* ptr = nullptr) : _ptr(ptr) { if (_ptr) { _count = new size_t(1); } else { _count = new size_t(0); } } SmartPointerx(const SmartPointerx& ptr) { if (this != &ptr) {//永遠成立
            this->_ptr = ptr._ptr; this->_count = ptr._count; (*this->_count)++; } } SmartPointerx& operator=(const SmartPointerx& ptr) { if (this->_ptr == ptr._ptr) { return *this; } if (this->_ptr) { (*this->_count)--; if (this->_count == 0) { delete this->_ptr; delete this->_count; } } this->_ptr = ptr._ptr; this->_count = ptr._count; (*this->_count)++; return *this; } T& operator*() { assert(this->_ptr == nullptr); return *(this->_ptr); } T* operator->() { assert(this->_ptr == nullptr); return this->_ptr; } ~SmartPointerx() { (*this->_count)--; if (*this->_count == 0) { delete this->_ptr; //數組內存泄漏 int*p = new int[10]
            delete this->_count; } } size_t use_count() { return *this->_count; } }; void TestSmartPtr() { { SmartPointerx<int> sp(new int(10)); SmartPointerx<int> sp2(sp); SmartPointerx<int> sp3(new int(20)); sp2 = sp3; std::cout << sp.use_count() << std::endl; std::cout << sp3.use_count() << std::endl; } //delete operator
} //---------------------------------------------------------------------------------------------- //下面是一個簡單智能指針的demo。智能指針類將一個計數器與類指向的對象相關聯, //引用計數跟蹤該類有多少個對象共享同一指針。每次建立類的新對象時,初始化指針並將引用計數置爲1; //當對象做爲另外一對象的副本而建立時,拷貝構造函數拷貝指針並增長與之相應的引用計數; //對一個對象進行賦值時,賦值操做符減小左操做數所指對象的引用計數(若是引用計數爲減至0,則刪除對象), //並增長右操做數所指對象的引用計數;調用析構函數時,構造函數減小引用計數(若是引用計數減至0,則刪除基礎對象)。 //智能指針就是模擬指針動做的類。全部的智能指針都會重載->和 * 操做符。 //智能指針還有許多其餘功能,比較有用的是自動銷燬。 //這主要是利用棧對象的有限做用域以及臨時對象(有限做用域實現)析構函數釋放內存
template<class T>
class SmartPointer final{ //final
    T* pobj = NULL; __int64* refCnt = 0; public: SmartPointer(T* pobj) {//這裏可能會傳一個棧對象地址
        if (pobj) { if (pobj != this->pobj) { if (!this->pobj) this->pobj = new __int64; (*refCnt)++; this->pobj = pobj; } } } SmartPointer(const SmartPointer<T>& rhs) { operator=(rsh); } SmartPointer<T> operator=(const SmartPointer<T>& rhs) { if (this == &rhs || pobj == rhs.pobj) return rhs; (*refCnt)--; (*rhs.refCnt)++; pobj = rhs.pobj; return *this; } ~SmartPointer() { refCnt--; if(refCnt == 0) ReleaseRes(); } T* GetPtr() const { return pobj; } private: void ReleaseRes() { if (pobj) { try { delete[] pobj; pobj = NULL; } catch (const std::exception&) { cout << "智能指針指向的不是一個堆對象" << endl; } } } }; #pragma endregion
#pragma endregion

#pragma region 2018.7.23
#pragma region 數組傳參方式

//方式一,數組引用傳遞
template<int N>
void ArrayRefAsParam(char(&_dest)[N]) {//數組引用的寫法
    char chs[] = "hello"; char* pstr = "hello"; cout << sizeof(chs) << endl; cout << strlen(chs) << ", " << strlen(pstr) << endl; strcpy_s(chs, "world"); cout << chs << endl; } //方式二,指針傳遞
void PointerAsParam(const char* pArr, int elemCount) { } void TestAstrPstr() { char chs[] = "world"; //6個字符,自動加了一個尾0 //1,數組引用傳參,如下兩種方式等價
    ArrayRefAsParam(chs); //模板不只能夠推導類型,也能夠推導出數組的大小
    ArrayRefAsParam<6>(chs); //說明了模板的工做原理,能夠不寫6,模板自動推導出數組大小 //2,指針傳遞
    int sz = sizeof(chs); //6
    int slen = strlen(chs); //5
    PointerAsParam(chs, 1 + strlen(chs)); } #pragma endregion
#pragma region 靜態(變量與函數)與常量(常引用,常指針,常函數)
class CWithConstStatic { private: static int _privateId; public: string _str = "CWithStatic";//C++11,能夠這樣初始化
     static string _sstr; //靜態變量不容許在類內初始化,這與舊C++一致
     int _id = 1010; public: static void StaticMethod(){ //1,靜態函數本質上是一個全局函數 //2,靜態函數不能訪問非靜態變量和非靜態函數,包括常函數及常量,由於它不屬於對象,沒有this指針,編譯器翻譯時出錯 // _id = 10; //不訪問非靜態變量,由於沒有this指針,不翻譯爲this->_id //ConstMethod();//不能訪問非靜態函數,由於沒有this指針,不翻譯爲 this->ConstMethod()
 } void ConstMethod() const {//1
        auto id = this->_id; StaticMethod(); //能夠訪問靜態函數,由於靜態函數不可能更改對象 //NormalMethod(); //不能訪問普通函數,由於普通函數可能會更改對象
 } void ConstMethod() { //注意1和2的兩個ConstMethod函數是重載關係
 } void NormalMethod() {//若函數從【調用1】處進入,則有:
        cout << "normal method begin" << endl; //輸出,沒問題 //cout << _id << endl; //出錯,由於這裏等價於 this->_id,而this指針爲NULL
 } }; string CWithConstStatic::_sstr; //靜態變量在類外的CPP中聲明
void NormalMethod(CWithConstStatic* _this) { } void TestCWithStatic() { //1,常對象
    const CWithConstStatic ow; //ow._id = 1001; //error, 常對象不能被修改 //ow._str = "dd"; //error, 常對象不能被修改
    ow._sstr = "dd"; //ok, 靜態變量不屬於對象 //2,常引用
    const CWithConstStatic& owRef = ow; //owRef._str = "hhh"; //error, 常引用不能被修改對象
    owRef._sstr = "dd"; //ok, 靜態變量不屬於對象 //3,常量指針,指向常量的指針,指向的內容不可修改
    const CWithConstStatic* pcwcs = new CWithConstStatic(); //pcwcs->_id = 20; //error,不可經過常指針更改其指向的內容 //4,指針常量,指針是一個常量,不可被再次賦值
    CWithConstStatic* const cpcwcs = new CWithConstStatic(); cpcwcs->_id = 20; //ok //5,類函數原理,this指針 //c++類的成員函數被編譯器翻譯爲了C語言編譯器能夠識別的全局函數,而後用C語言編譯器來處理它 //如下兩條調用等價
    CWithConstStatic* pwcs = NULL; pwcs->NormalMethod(); //【調用1】C++的樣子
    NormalMethod(pwcs); //【調用2】C語言翻譯出來的結果
 } #pragma endregion

#pragma region 深刻模板
#pragma region 可變參數模板
void TestVarTemp() {//【無參的重載函數】 //這個函數必須定義,不然編譯器報錯,由於函數參數展開時,最終(可變參數個數爲0時)要調用此函數
} template<typename First, typename... Args >
void TestVarTemp(First first, Args... args) { //sizeof...是可變參數模板專用的獲取參數個數的函數
    cout << sizeof... (args) << "-" << first << " "; //可變參數展開的惟一方式是遞歸調用,一層層剝離參數,當參數個數爲0時調用無參的重載函數,見【無參的重載函數】
 TestVarTemp(args...); } void TestVarTemplate() { TestVarTemp(1, 2, 3, 4, "hello"); } #pragma endregion
#pragma endregion
#pragma region 構造和拷貝構造
class CNormclass { public: CNormclass() { cout << "constructor" << endl; } CNormclass(const CNormclass& rhs) {//有了複製構造函數後,系統再也不爲類生成無參構造函數
        cout << "copy-constructor" << endl; *this = rhs; } }; CNormclass TestConstructorAndCopyCon1() { return CNormclass();//不調用COPY構造函數
} CNormclass TestConstructorAndCopyCon2() { //對象定義:兩種不一樣的定義方式 //方式一,會調用兩次構造函數
    CNormclass r0; //constructor
    r0 = CNormclass(); //constructor,注意不是COPY構造函數 //方式二,只調用一次構造函數
    CNormclass rr = CNormclass(); //constructor //COPY構造函數僅在兩種狀況下調用: //1,將一個已存在的對象生成另一個對象
    CNormclass r1 = r0; //拷貝構造 //2,將一個已存在的對象做爲參數傳遞給構造函數時
    CNormclass r2(r0);    //拷貝構造 //不調用構造函數,也不調用拷貝構造函數,也不調用=運算符(由於是同類型),只是進行按位copy
    r1 = r0; cout << "before return " << endl; return rr;    //調用COPY構造函數
} #pragma endregion
#pragma region 函數指針複雜嵌套 typedef int(*PF2)(int); typedef PF2(*PF1)(int, int); typedef PF1(*PF)(int); int func2(int) { cout << "func2" << endl; return 0; } PF2 func1(int, int) { cout << "func1" << endl; return func2; } PF1 funcx(int) { cout << "funcx" << endl; return func1; } void TestNestingFuncPtrs() { //1,一次嵌套
    PF1 pf1 = func1; pf1(1, 2)(1); //等價方式的直接聲明
    int(*(*ptr)(int, int))(int) = func1; ptr(2, 3)(4); cout << "--------------------" << endl; //2,二次嵌套
    PF pf = funcx; pf(1)(2, 3)(2); //等價方式的直接聲明
    int(*((*((*ptr2)(int)))(int, int)))(int) = funcx; ptr2(1)(2, 3)(2); } #pragma endregion
#pragma region 類型轉換構造函數
class CTypeCast { public: int _id; string _name; CTypeCast(int i) {//整形轉換構造函數:將一個整形轉爲對象
        _id = i; cout << "integer cast " << i << endl; } CTypeCast(string str) {//字符串轉換構造函數:將一個字符串轉爲對象
        _name = str; } //注意,顯示聲明,轉換必須顯式進行
    explicit CTypeCast(float fx) {//浮點轉換構造函數:將一個字符串轉爲對象
        cout << "float cast " << fx << endl; } }; void TestTypecastContructor() { //CTypeCast otc = 1; //整形轉換構造函數 //CTypeCast otc2 = "otc2"; //字符串轉換構造函數 //otc = 3; //注意,當加了explicit後,類型轉換必須顯示進行,所以下面這個語句不會使用浮點轉換構造函數 //可是,它卻可使用整形轉換構造函數,這會形成數據精度丟失
    CTypeCast otc3 = 3.2f; //隱式轉換:整形轉換構造函數
    CTypeCast otc4(3.2f);  //顯示轉換:浮點轉換構造函數
 } #pragma endregion

#pragma region 2018.7.24
#pragma region 類型轉換運算符及()[]重載
class CTypeCastOper{ float fx = 0.2f; int arr[3]{ 1,2,3 }; public: //1,類型轉換運算符
    explicit operator float() { return fx; } operator string() { } //2,()重載 //()運算符並非用來作類型轉換的,它是當函數用的,即仿函數,或函數對象
    bool operator()() { return true; } //3,[]重載 //[]運算符與()差多的用法,都是用於對象以後
    int operator[](int idx) { return arr[idx]; } }; void TestTypecastOper() { CTypeCastOper oper; float fx = (float)oper; cout << fx << endl; //1,()運算符
    bool b = oper(); //2,[]運算符
    cout << oper[0] << "," << oper[1] <<"," << oper[2] << endl; } #pragma endregion
#pragma region 模板特化 template<typename T>
class CTehuaTemp { public: T px = "abc";//2,被特化爲了一個char*類型指針,故能夠這樣用
}; template<typename T>
class CDThhuaTemp : public CTehuaTemp<T*> {//1,將基類模板參數特化爲一個指針類型
public: T ch = 'c'; }; void TestTehuaTemp() { CDThhuaTemp<char> otp; cout << otp.px << endl; cout << otp.ch << endl; } #pragma endregion
#pragma region 同類型賦值,常引用修改
class CSimpleclass { public: CSimpleclass() { cout << "cons" << endl; } CSimpleclass(const CSimpleclass& rhs) { cout << "copy cons" << endl; } public: float fx = 0; //默認未初始化,給它來個初始化
}; void TestSameTypeAssign() { CSimpleclass oc, oc1; const CSimpleclass& oc2 = oc; const CSimpleclass& oc3 = oc; cout << "-------------------------" << endl; //【同類型賦值,不調用=運算符,也不調用任何構造函數】
    oc1 = oc; //oc2 = oc3; //常引用自己是個常量,也不能被修改 //oc2 = oc1; //常引用自己是個常量,也不能被修改 //oc2.fx = 30; //常引用不能更改引用的對象內容

    const std::string ss; //ss = "abc"; //wrong //ss.clear(); //wrong
} #pragma endregion
#pragma region 堆指針棧指針判斷
class CTestPointerType { public: CTestPointerType(float fx=0) { this->fx = fx; } float fx; }; template<class T, int N>
class CHeapDebugger { public: static void Print(const T* p){ int sz = N * sizeof(T); int* ip = (int*)p; int headFlag = *(ip - 1); int endFlag = *(int*)((char*)ip + sz); int orderFlag = *(ip - 2); int szFlag = *(ip - 3); bool isHeapPtr = headFlag == endFlag && headFlag == 0xfdfdfdfd && sz == szFlag; cout << "----------------------------------------------" << endl; //if (isHeapPtr) {
            cout << hex << "堆大小:" << szFlag << endl; cout << "堆編號: " << orderFlag << endl; cout << "堆首界: " << headFlag << endl; cout << "堆尾界: " << endFlag << endl; //} //else { // cout << "棧指針" << endl; //}
        cout << "----------------------------------------------" << endl; } }; void TestPointerType() { //     const int N = 4; int*p = new int[N]; for (int i = 0; i < N; i++) { p[i] = i; } CNormclass* pn = new CNormclass[N]; CTestPointerType*po = new CTestPointerType[N]; const int*pc = &N; CHeapDebugger<int, 1>::Print(&N); int a = 10, b = 11; float fx = 20, fy = 30; CHeapDebugger<int, 1>::Print(&a); CHeapDebugger<int, 1>::Print(&b); CHeapDebugger<float, 1>::Print(&fx); CHeapDebugger<float, 1>::Print(&fy); delete po; } #pragma endregion
#pragma endregion

#pragma region 右值引用和MOVE
void TestRef(){ int a = 0, b = 1; int& ra = a; cout << ra << endl; //0
    ra = b; //此時ra不是a的引用也不是b的引用,而是一個普通變量
    b = 300; cout << ra << endl; //1
 } #pragma endregion
#pragma region C11智能指針

#pragma endregion
#pragma region 正則表達式

#pragma endregion
#pragma region lambda表達式

#pragma endregion
#pragma region unorder_map及hashtable實現 
//有沒有無衝突哈希算法

#pragma endregion
#pragma region DIJKASTRA最短路徑算法

class Obj { public: Obj(float fx) { x = fx; } float x; }; bool cmpfunc(Obj a, Obj b) { return a.x < b.x; } void TestStlSortFunc() { std::vector<Obj> vec; vec.push_back(Obj(1)); vec.push_back(Obj(12)); vec.push_back(Obj(1.3f)); vec.push_back(Obj(2.31)); vec.push_back(Obj(31)); vec.push_back(Obj(4)); vec.push_back(Obj(0)); int ax = 123; auto iter = max_element(vec.begin(), vec.end(), [ax](Obj obj1, Obj obj2){ cout << "cap addr of ax : " << ax << endl; return obj1.x < obj2.x; }); cout << (*iter).x << endl; } void RemoveVecElem(std::vector<int>& v, int e) { for (auto it = v.begin(); it != v.end();) { if (*it == e) { it = v.erase(it); break; } else it++; } } void Dijkastra() { const int m = 99999; const int n = m; const int nodeCount = 7; int paths[][nodeCount] = { { n, 50, 12, m,  45, m, m }, { m, n, m, m, 2 , m, m }, { m, 10, n,  99, m , m, m }, { m, m, m, n, m , m, m }, { m, m, m, 10, n , m, m }, { m, m, m, m, 0 , n, 1 }, { m, 1, m, m, m , m, n }, }; std::vector<string> sel; std::vector<int> left{ 0, 1, 2, 23, 4, 15, 6 }; sel.reserve(8); left.reserve(8); int startIdx; cout << ">> 選擇一個起點 " << endl; cin >> startIdx; cout << ">> v" << startIdx << endl; if (startIdx >= nodeCount) return; RemoveVecElem(left, startIdx); cout << "after erase : " << left.capacity() << endl; for (auto e:left) { cout << e << ","; } cout << endl; cout << ">> calculating ..." << endl; int tmp[nodeCount]; for (int i = 0; i < nodeCount; ++i) { tmp[i] = paths[startIdx][i]; } std::stringstream ss; //ss >> "v" >> startIdx;
 auto iter = min_element(tmp, tmp + nodeCount); cout << *iter << "," << iter - tmp << endl; int curMinNode = iter - tmp; int curMinPathLen = *iter; // ss >> "->v" >> curMinNode; //sel.push_back(ss.str()); //ss.clear();
 RemoveVecElem(left, curMinNode); while (left.size() > 0) { bool isfind = false; for (int i = 0; i < nodeCount; ++i) { int p1 = paths[startIdx][i]; for (int j = 0; j < nodeCount; ++j) { bool isold = false; for (int i = 0; i < left.size(); ++i) { if (left[i] == j) isold = true; } if (!isold) { int p2 = paths[curMinNode][j]; if (j != curMinNode) { //j != curMinNode
                        if ((curMinPathLen + p2) < p1) { isfind = true; paths[startIdx][i] = (curMinPathLen + p2); } } } } } if (left.size() == 0)break; auto p = paths[startIdx]; auto iter2 = std::min_element(left.begin(), left.end()); curMinPathLen = *iter2; //curMinNode = iter2 - left.be;
 RemoveVecElem(left, curMinNode); cout << "left: " << left.size() << endl; } // sel.push_back(0); // sel.erase(sel.begin()); // sel.shrink_to_fit(); // cout << "cap: " << sel.capacity() << endl; // for (int d : sel) // { // cout << d << endl; // } // cout << sel.size() << endl;
} #pragma endregion
#pragma region EffectiveC++
namespace EffectiveCpp { #pragma region 02-以const,enum,inline替代define
    class CStaticConst { public: //【1】,static const 能夠同時存在,這在C#中是不容許的 //在C#中,常量也是屬於類而不屬於對象,這就等價於C++的 static cosnt 合體了
        static const float fx; //【聲明式】 //【2】,浮點類型,不能在定義時初始化 //static float fx2 = 3; //【錯誤】 //【3】,整數類型(整形,char,枚舉),能夠在定義時初始化,且不須要在類外寫定義式
        static const int ix = 3; //聲明並初始化,注意,這不是定義,也就是說聲明時能夠賦值

        enum {NumTurns = 5}; int scores[NumTurns]; //enum hack //【不安全宏的替代品】,既有宏的高效率和函數的安全性
        template<typename T> inline T safe_max(const T& a, const T& b) { return a > b ? a : b; } virtual void VFunc() { } }; const float CStaticConst::fx = 1; //【定義式】:不能寫static //const int CStaticConst::ix = 3; //【錯誤】,已經初始化過了,不能重複
    const int CStaticConst::ix; //定義式,聲明時已初始化了。由於是整數類型,這個定義式能夠不寫 //1,【宏是不安全的】任什麼時候候都不要忘了給宏的實參加上() //2 替代方法:使用 template inline
#define unsave_max(a, b) (a) > (b) ? (a) : (b) 

    void Test02() { CStaticConst oc; cout << oc.fx << endl; int a(10), b(20); //不安全的宏,下面這樣的致使b被加兩次
        max(++a, b++); cout << "a=" << a << ", b=" << b << endl; string s1 = "hello"; string s2 = "hello"; } #pragma endregion } #pragma endregion x

#pragma region 2018.8.2
#pragma region 協變逆變 template<typename T> 
class CAnimal { public: void Print(T info) { cout << info << endl; } }; template<typename T>
class CHuman : public CAnimal<T> { }; void TestXiebian() { int a[10], b[10]; float c[10]; int*p = a; int*p2 = b; //int*p3 = c; //error
 A* pa = new B[10]; //協變 //協變在C++模板中彷佛不能用,不像C#
    CAnimal<A>* panim = new CHuman<A>[10]; //正確,這個仍是普通的數組協變,而不是像C#那樣真正的泛型協變 //CAnimal<A>* panim = new CAnimal<B>[10]; //錯誤 //CAnimal<A>* panim = new CHuman<B>[10]; //錯誤
 } #pragma endregion
#pragma endregion

#pragma region 2018.8.3
#pragma region 奇怪的默認構造函數和子父間對象賦值

class CA { public: float fx; CA(float x) { fx = x; } virtual void tostring() /*final*/ {//可加final禁止重寫
        cout << "ca" << endl; } }; class CB : public CA { public: float fbx = 2; explicit CB() :CA(1) { } //拷貝構造函數能夠有兩個,常量的,很是量的
    CB(CB& b) : CA(1) {//拷貝構造函數1,
        cout << "copy 1" << endl; } CB(const CB& b) : CA(1) {//拷貝構造函數
        cout << "copy 2" << endl; } // void tostring() { // cout << "ca" << endl; // }
}; CB b() { cout << "....function...b...." << endl; return CB(); //【標記1】調用無參構造函數
} void TestOddConstructor() { CA a(1); CB b(); //調用無參構造函數??錯,實際上是聲明瞭一個返回值爲B的函數o(),對比【標記1】
    CB bx; //ok,調用默認構造函數構造了一個對象bx
    CB* pb = new CB(); //ok,調用默認構造函數構造了一個對象 //C++中,子對象能夠賦值給父對象,C#中也能夠,不過C#操做的是指針
    a = bx; //子對象能夠直接賦值給父對象,發生類截斷,子類部分被丟棄 //bx = (CB)a; //error,編譯出錯,不能轉換,除非自定義轉換 //bx = static_cast<CB>(a);
 CB rb = b(); cout << rb.fx << endl; //正確輸出: 1 //CB* pb1 = new CB; //ok,調用無參構造函數 //CB* pb2 = new CB(); //ok, 調用無參構造函數
 CB ob1; const CB ob2; //拷貝構造函數能夠有兩個,常量的,很是量的
    CB ob3(ob1); //copy 1
    CB ob4(ob2); //copy 2
 } #pragma endregion 
#pragma region 四種類型轉換
class CMemory { public: int x; float fx; char ch = 'a'; }; class ConstFuncForcemodify { float fx = 998; public: void PrintNum() const { cout << --(const_cast<ConstFuncForcemodify*>(this))->fx << endl; //去除常量性,或直接使用舊式強轉 //cout << --((ConstFuncForcemodify*)this)->fx << endl;
 } }; void Test4Typecast() { CA a(1); CB b; //------------------------------------------------------------------ //一,靜態類型轉換 static_cast, 【任何類型:值,指針或引用】 //不能將const類型轉爲non const類型 //1,普通類型轉換
    int i = static_cast<int>('a'); //等價x = (int)'a';
    char c = static_cast<char>(i); //等價c = (char)x; //a = b; //自己能夠轉換,子類轉換父類
    a = static_cast<CA>(b); //ok
    a = static_cast<CB>(b); //ok //b = static_cast<CB>(a); //error,編譯錯誤 //2,指針類型轉換,只檢查有無父子關係,不進行動態類型檢查 //動態類型即運行時,指針實際指向的類型
    CB* pb = static_cast<CB*>(&a); //ok,但運行時訪問CB類(不屬於CA)的部分將出錯
    pb = (CB*)&a; //等價於上式
    cout << pb->fbx << endl; //輸出一個未初始化的或不可預料的值
    CA* pa = new CB(); pb = static_cast<CB*>(pa); cout << pb->fbx << endl;  //ok //------------------------------------------------------------------ //二,動態類型轉換 dynamic_cast,【用於指針或引用】 //【基類必須定義了虛函數,說明動態時類型檢查是根據虛函數表來作的】
    pb = dynamic_cast<CB*>(&a); //編譯OK,但運行時轉換失敗pb = Null //pb = (CB*)&a; //等價於上式
    if(pb)pb->tostring(); //------------------------------------------------------------------ //三,從新解釋轉換reinterpret_cast, 【任何類型:值,指針或引用】 //依賴於編譯器,不可移植
    void (*pf)() = Test4Typecast; int* ipf = (int*)pf; int ix = reinterpret_cast<int>(pf); //將指針轉爲int,或直接轉換
    ix = (int)pf; cout <<hex << "ix: " << ix << ", addr: " << pf << endl; int* pix = reinterpret_cast<int*>(102);//將整形轉換爲指針,或直接轉換
    pix = (int*)102; //------------------------------------------------------------------- //四,常量指針轉換,移除指針的常量性, 【用於指針或引用】
    const int ctx = 10; int* itx = const_cast<int*>(&ctx); //將常指針轉爲int*指針,或直接轉換
    itx = (int*)&ctx; //測試經過轉換去除常指針,在類的常函數中修改爲員變量的值
 ConstFuncForcemodify ocffd; ocffd.PrintNum(); //997
    ocffd.PrintNum(); //996
    ocffd.PrintNum(); //995
} #pragma endregion 
#pragma region 內存佈局-參考[堆指針棧指針判斷]
//1,程序總佈局,內存從高地址向低地址分配, //2,對象,結構,數組內部的元素從低地址向高地址分配 //分配順序是變量定義的前後順序 //本機測試中,任何兩個獨立數據間的地址間隔爲12
class CT { public: int x = 101, y = 102, z = 103;//默認公有
    void testd() { cout << x << "<" << y << "<" << z << endl; } }; class MyStruct : public CT { public: //=========================================================================== //通用規則,基類指針或引用只能看到基類變量與函數,C++與C#都是這樣 //=========================================================================== //如:CT * pt = new MyStruct(),則pt->只能看到基類數據與函數
    int y = 1, z = 2, w = 3, h = 4, k = 5; //與基類重名,pt->x是基類的,pt->w則錯誤,找不到這個變量
}; void TestMemLayout() { //不相關的各元素,內存從高到低分配,不連續
    int x, y, z, w, o, p, q, r, s; cout << "addr>>" << &x << "," << &y << "," << &z << "," << &w << "," << &o << "," << &p << "," << &q << "," << &r << "," << &s << endl; //分配完上面的一些獨立數據後,內存地址減12,繼續分配數組 //數組以內,各元素內存地址從低到高,連續,此時數組的最大地址要小於上面已分配的最小地址
    int iarr[10]; for (int i = 0; i < 10; ++i) { cout << &iarr[i] << endl; } //分配完上面的數組,內存地址減12字節,繼續分配對象數據 //對象內,各元素內存地址從低到高,連續,最大地址小於上面的最小地址
 CMemory om; cout << &om.x << endl; cout << &om.fx << endl; cout << &om.ch << endl; //輸出亂碼,對字符地址操做默認爲是輸出字符串
    cout << (char*)&om.ch << endl; //同上輸出亂碼,對字符地址操做默認爲是輸出字符串
    cout << (int*)(&om.ch) << endl; //輸出地址 //========================================================================================= //內存覆蓋實驗 //利用上面的12差規律,覆蓋內存數據 //12差規律,也就是說每兩個不相關的數據間有8個字節用來CCCC填充,表示內存分界,內存分界不能寫入,不然崩潰 //=========================================================================================

    int poorA = 1; int poorB = 2; int poorC = 4; //========================================================================== //CT* pt = new CT(); //堆上數據被默認初始化爲0: x=y=z=0
    CT ot;//棧上數據未初始化 //========================================================================== //查看彙編代碼時能夠發現,編譯器每進一個函數時就會行標記一段內存,每一個字節能寫入0XCC,這個值就是21號中斷 //這樣作是爲了給開闢的內存作上標記,只容許編譯器來在這段內存上分配內存,不容許非法的操做 //如,程序定義了 int a=1,則編譯器給 a分配4個字節,並將1寫入,若經過指針訪問未被賦值的內存,則它裏面的內容就是0xcc //,訪問時當即觸發21號中斷,程序報錯,內存不可訪問
    MyStruct* pms = static_cast<MyStruct*>(&ot); //或 pms = (MyStruct*)(&ot) //pms->y = 11; //觸發21中斷 //pms->z = 12; //觸發21中斷
    pms->w = 0; //不是內存分界,能夠覆蓋,這個就是poorC所在位置
 cout << hex << "-----------------------" << endl; int* ptt = (int*)&(pms->x); for (int i = 0; i < 15; ++i) { cout << *(ptt + i) << endl; } //poorC的值被改成了0
    cout << poorA << "," << poorB << "," << poorC << endl; } #pragma endregion
#pragma region 無題
const int* TestConstarr() { int* iarr = new int[3]{ 1, 2, 3 }; return iarr; } template<int> //無心義的全特化,C#不支持
void TestAllConcreate() { } #pragma endregion
#pragma endregion

#pragma region 2018.8.4
#pragma region 靜態函數重載
class CSox { public: static void f1(int x) { cout << "f1 int" << endl; } static void f1(float x) { cout << "f1 float" << endl; } }; #pragma endregion
#pragma region 有符號無符號賦值
void TestSignedUnsignedAssign() { //1,vs2017c++默認容許進行數據截斷,即double賦值給int可行
    int ix = 2.3; //ok, 容許數據截斷
 cout << hex; unsigned int ui = 0; //2,有符號無符號混合運算 //計算過程: //ui-1,轉換爲 ui + (-1) //因爲 -1默認爲int, uint + int 類型自動轉換爲uint //-1的補碼爲 0xffffffff,故 0 + 0xffffffff = 0xffffffff,對應無符號值仍是0xffffffff
    cout <<  ui - 1 << endl;//0xffffffff //3,越界表現
    int x = 65536; //2的16次方
    int y = x * x; // 0, 緣由:2的32次方是0x100000000,超出了32位,故截斷爲0
    int y1 = (double)x*x*x*x; //0x80000000
    int y2 = (double)(x*x); //0,緣由:(x*x)這個單元在計算時是按int算的,自身算完後截斷爲0,再轉double已經晚了
    int y3 = double(x)*x; double dx = double(x) * x; int y4 = *(int*)&y3; int z = pow(2, 32);//0x80000000, 緣由同y1
    int w = 2 ^ 3; //注意,這是異或,不是2的3次方
 cout << hex<< y << "," << y1 << "," << y4 << "," << z << endl; cout << "------------------------" << endl; double a = 0x13edf000000011; cout << "flaot a " << a << dec << "," << 0x01ffffff << endl; unsigned int i = a; //printf("a = %lx\n", a);
    cout << hex<< "i=" << i << endl; cout << dec << 0x33333333 << endl; } void testcasttype() { int i = 0xffffffff;  //-1
    int j = 0xfffffffe; //-2
    cout << "i=" << i << endl; double di = i; cout << di << endl; auto typ1 = 0x8000; //int
    auto typ2 = 0x7fffffff; //int
    auto typ3 = 0x80000000; //unsigned int
    auto typ4 = 0xffffffff; //unsigned int
    auto typ5 = 0x100000000; //long long //注意,=號右邊表達式中的數據類型與=號左邊的類型無關,以下計算等價於 //double = uint * int
    double multype = 0x80000000 * 0x7fffffff; //double 轉int //轉換過程:double = uint * int //=號右邊是按最大類型uint 來算的 //uint 類型的0x80000000左移一位後出了uint範圍,變爲0
    double td1 = 0x80000000 * 2; //int轉爲double後,總能正確的轉回來,前提:數據在int型數據範圍內 //當一個數越出了int型表示範圍,轉換爲int時,總會獲得0x80000000 //1,double x = 0x2345e0f0d01f; (int)x ==> 0x80000000 //2,float x = 0x2345e0f0d01f; (int)x ==> 0x80000000 //當一個數越出了uint型表示範圍,轉換爲uint時,獲得被截斷的低32位數據 //1,double x = 0x2345e0f0d01f; (int)x ==> e0f0d01f //2,float x = 0x2345e0f0d01f; (int)x ==> e1000000 //注意,因爲float的尾數只有23位可表示,所以截取數據會有偏差,如上x只能精確到2345e0這24位,所以結果爲e1000000 //雖然float的表示範圍很大,但int轉爲float後再轉回來就可能丟失數據 //即當int數據小於23位時,能夠安全的轉爲float,而後再原樣轉回
    int imax = 0x7fffffff; cout << imax << endl; cout << hex << (int)td1 << endl; cout << hex << (unsigned int)td1 << endl; float td2 = float(0x2345e0f0d01f); cout << hex << (int)td2 << endl; cout << hex << (unsigned int)td2 << endl; cout << hex << (short)td2 << endl; cout << hex << (unsigned short)td2 << endl; cout << hex << (int)(char)td2 << endl; cout << hex << (int)(unsigned char)td2 << endl; int x = 65536; int y3 = double(x)*x; double y4 = double(x)*x; //double y5 = 
    cout << *(int*)&y3 << endl; } void tstcast() { int a = 0x60000000; float *p = (float*)&a; cout << *p << endl; printf("%f\n", (float)(*p)); //0.000000 //printf("%f\n", (double(a)));//10.000000
} #pragma endregion
#pragma region 數組和指針
void TestArrayAndPtr() { short ac[] = { 1, 2, 4, 0, 3, 5, 7, 9 }; //數組
    short* parr = new short[8] { 1, 2, 4, 0, 3, 5, 7, 9 };//指向數組的指針 //----------------------------------------------------------------------------------- //數組比較特別,沒法按照普通指針那樣用 &p,p,*p的模式去通解它,只能分2種狀況去理解,以下1,2 //----------------------------------------------------------------------------------- //1,數組名指向數組首元素, 這與指向數組的指針表現同樣
    cout << *(ac) << endl;//1
    cout << *(ac + 1) << endl; //2
    cout << *(parr) << endl; //1
    cout << *(parr+1) << endl;//2 //2,&數組名取得數組內存塊的地址,而&指針取得指針的地址
    cout << &ac << endl; //003CF830
    cout << &ac + 1 << endl; //003CF840,內存偏移了16字節,即數組大小
    cout << &parr << endl; //003CF824
    cout << &parr + 1 << endl; //003CF828 //內存偏移了4個字節,任何指針大小都是4個字節 //3,數組指針,也叫行指針
    short(*parr1)[8] = &ac; //指向具備8個short類型的數組
 } #pragma endregion
#pragma region const函數重載與常對象函數調用
class constoverload { char _vals[10] = { 1, 2, 3, 4, 5, 6 }; public: //利用const 進行函數重載 //1,以下 不算重載,算同一個函數,重定義會報錯 //void f2(const float i){}
    void f2(float i) {} //2,利用const進行重載
    void f1(const int i) const { cout << "const f1" << endl; } void f1(int i) { cout << "just f1" << endl; } //3,重載【運算符重載函數】
    const char& operator[](int idx) const {//返回值是引用,必須加const修飾,不然違反了常函數規則
        cout << "const operator[]" << endl; return _vals[idx]; } char& operator[](int idx) { //不是常函數,能夠返回非const的引用 //注意,這裏的返回值爲引用,是爲了使用 obj[idx] = 'x'操做,若非引用則不合法
        cout << "operator[]" << endl; return _vals[idx]; } }; void Print(const constoverload& ctl) { auto x = ctl[0]; //const operator[]
} void TestConstOverload() { //----------------------------------------------------------------------- //常對象只能調用常函數,由於很是函數可能會更改了對象內的值 //-----------------------------------------------------------------------
    const int ix = 10; constoverload od; const constoverload cod; od.f1(1); //just f1
    od.f1(ix); //just f1,注意這個調用,並不會由於參數是const int而去調用形參爲const int的那個常函數
    cod.f1(2); //const f1 //cod.f2(1); //error, 編譯錯誤
 od[0] = 'a'; //由於operator[]返回了引用,故可修改其返回值 //真實使用情形
    Print(od);//const operator[] //也能夠這樣
    Print(cod);//const operator[]
} #pragma endregion
#pragma endregion
#pragma region 2018.8.6
#pragma region 線程基本使用
void ThreadFunc(int t) { this_thread::sleep_for(chrono::milliseconds(t)); //this_thread::yield(); //獲取線程ID
    cout << "thread started:" << this_thread::get_id() << "," << _threadid << endl; } void TestThread() { std::thread t(ThreadFunc, 300);//可變參數
 t.join(); //t.join();//1已經結束了,不能夠再操做它,不然異常
    std::thread t1(ThreadFunc, 1000); //t1.detach(); //解除join,將當前線程做爲後臺線程,主線程結束後將報錯
    t1.join(); //再join會異常,已解除了不能再join
} #pragma endregion
#pragma region 線程同步
int totalNum = 999; std::mutex banklock; void DrawbackMoney() { banklock.lock(); if (totalNum < 0) return; banklock.unlock(); if (totalNum < 0) { throw std::exception("錢的數目小於0");//彈出框不顯示此字符串???
 } for (int i = 0; i < 20; ++i) { banklock.lock(); if (totalNum > 0) { cout << "left: " << totalNum << ", thread:" << this_thread::get_id() << endl; totalNum -= 10; this_thread::sleep_for(chrono::milliseconds(100)); } else { banklock.unlock(); return; } banklock.unlock(); } } void TestThreadSync() { thread t1(DrawbackMoney); thread t2(DrawbackMoney); thread t3(DrawbackMoney); thread t4(DrawbackMoney); thread t5(DrawbackMoney); thread t6(DrawbackMoney); thread t7(DrawbackMoney); thread t8(DrawbackMoney); thread t9(DrawbackMoney); this_thread::sleep_for(chrono::seconds(3)); t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join(); t7.join(); t8.join(); t9.join(); } #pragma endregion
#pragma region 智能指針使用
class managedOjbect { public: float fx; double dx; managedOjbect() { cout << "managed object constructor" << this << endl; } ~managedOjbect() { cout << "managed object destructor" << this << endl; } }; void TestShareptrUse() { //1,多個智能指針間能夠相互賦值,智能指針內部自動處理引用計算
    std::shared_ptr<managedOjbect> ptr(new managedOjbect); std::shared_ptr<managedOjbect> ptr1(ptr); //ok, ptr1與ptr引用同一個對象
    ptr->fx = 10; ptr1 = ptr; ptr = ptr1; std::shared_ptr<managedOjbect> ptr2(); //這是函數聲明式,不是指針!!! //2,不要將一個裸指針同時託管給多個智能指針,不然將發生重複析構
    auto* pobj = new managedOjbect(); std::shared_ptr<managedOjbect> ptr3(pobj); //std::shared_ptr<managedOjbect> ptr4(pobj); //析構異常,重複delete
 auto* pobj1 = new managedOjbect(); auto* pobj2 = new managedOjbect(); std::shared_ptr<managedOjbect> ptr5(pobj1); //3,儘可能不要使用reset,由於它會釋放託管對象,會致使其它智能指針異常
    ptr5.reset(); //當即釋放託管堆,即delete pobj1
    ptr5.reset(new managedOjbect()); //當即釋放原來的,持有新的對象 //4,智能指針不能用於數組 //std::shared_ptr<managedOjbect> arrptr(new managedOjbect[10]); //析構時異常,緣由見下面異常分析 //std::shared_ptr<int> iarptr(new int[10]); //

                                     //----------------------------------------------------------------------------------- //析構異常分析: //釋放數組時應使用 delete[],不該該使用delete //----------------------------------------------------------------------------------- //1,對於基本類型測試 delete與delete[]等價
    int* pia = new int[10]; int* piab = pia; //&(pia[0]);
    for (int i = 0; i < 10; ++i) pia[i] = i; delete pia; //&(pia[0]); //運行時不報錯,數組內存被正確,徹底釋放
    pia[1] = 0xff; //運行時出錯,由於指針已經是野指針了
    piab[1] = 0xff; //正確運行

    for (int i = 0; i < 10; ++i)//查看內存
        cout << hex << piab[i] << ","; cout << endl; //2,對於自定義類型,應使用delete[] //智能指針只處理單個類對象,而不處理數組,所以託管數組時析構異常,就是由於調用了delete而不是delete[]
    auto* pobjs = new managedOjbect[10]; delete pobjs; //運行時崩潰,不是一個有效的堆指針,應使用delete[] pobjs;
 } #pragma endregion
#pragma region 自已實現智能指針 template<typename T>
class SmartPtrl { std::size_t* psz = nullptr; T* _ptar = nullptr; public: string name; SmartPtrl(T* p) { //帶裸指針的構造函數只能是初始構造者,不能將同一個裸指針傳遞給兩個不一樣的智能指針 //由於裸指針不知道如何去減小引用計數
        psz = new std::size_t; *psz = 1; _ptar = p; } SmartPtrl(SmartPtrl& ptr) { operator=(ptr); } SmartPtrl& operator=(SmartPtrl& ptr) { if (this == &ptr || _ptar == ptr.get()) return *this; if (_ptar) { decrease(); } _ptar = ptr.get(); psz = ptr.psz; ++(*ptr.psz); } T* operator->() { return _ptar; } T& operator*() { return *_ptar; } ~SmartPtrl() { cout << "destructor: " << name << endl; if (_ptar) decrease(); } T* get() { return _ptar; } void decrease() { if (nullptr == psz)return; (*psz)--; if (*psz == 0) { delete _ptar; delete psz; _ptar = nullptr; psz = nullptr; } } }; void TestMySmartPtr() { SmartPtrl<managedOjbect> ptr(new managedOjbect); ptr.name = "ptr"; SmartPtrl<managedOjbect> ptr2(ptr); ptr2.name = "ptr2"; //自賦值及相等指針賦值測試
    ptr2 = ptr; ptr = ptr2; ptr = ptr; SmartPtrl<managedOjbect> ptr3(new managedOjbect); ptr3.name = "ptr3"; SmartPtrl<managedOjbect> ptr4(new managedOjbect); ptr4.name = "ptr4"; ptr3 = ptr4; //重載的指針運算符使用起來有點奇怪,以下兩種等價 //按常理,operator->應該返回一個引用而不是指針
    ptr3->dx = 30; //從這個來看,->返回的應該是一個引用
    ptr3.operator->()->dx = 30; //這纔是它的本質
 cout << ptr4->dx << endl; cout << (*ptr4).dx << endl; //operator*
 } #pragma endregion
#pragma region delete和delete[]的表現
void testDeleteStack() { //1,測試1
    managedOjbect* pobj = new managedOjbect(); //delete[] pobj; //不報錯,運行時不斷析構 //2,測試2
    managedOjbect* pt = &(pobj[100]); //delete pt; //不報錯,運行無錯 //delete[] pt; //不報錯,運行時不斷析構 //3,測試3
 managedOjbect mobj; //delete[] & mobj; //不報錯,運行時不斷析構 //4,測試4
    managedOjbect* pobjs = new managedOjbect[3]; //delete pobjs; //運行時異常 //5,查看內存頭,數據彷佛也正常
    CHeapDebugger<managedOjbect, 1>::Print(pobj); //總結: //對於基本類型,使用 delete[]萬無一失 //對於類對象類型,單對象使用delete,數組對象使用delete[],不然出錯 //對單對象使用delete[],將出現上面的死循環析構 //對數組對象使用delete,將拋出運行時異常
 } #pragma endregion
#pragma region 模板特化
//------------------------------------------------------------------------------------------ //模板特化的原由是:某些特定類型不適用於模板的通用算法,須要特化出一個專用版原本處理它(寫個特定算法) //同時,因爲模板不支持定義類名相同而類型個數不一樣的模板 //對於模板類型參數個數可變的需求,C++11提供了typename...語法支持可變類型參數 //------------------------------------------------------------------------------------------ //1,模板特化是指在原始模板的基礎上,特化出一個同名的模板來,只是參數被特化了 //注意模板特化的語法是在類名後加<>並指定特化類型
template<typename T, typename T2>
class tehuaTemp {//原始版本
public: T Max(T a, T b) { cout << "max a b" << endl; return a > b ? a : b; } }; //2,只能定義一個模板原始版本,不能經過增長或減小模板類型參數個數來定義不一樣的同名模板 // template<typename T, typename T2, typename T3> //不能這樣作, // class tehuaTemp{ // public: // T Max(T a, T b) { // cout << "max a b" << endl; // return a > b ? a : b; // } // }; //3,這不是模板特化 // template<typename T, typename T2, typename T3> // class tehuaTemp<T, T2, T3> {//原始版本 // public: // T Max(T a, T b) { // cout << "max a b" << endl; // return a > b ? a : b; // } // };
template<typename T>//偏特化
class tehuaTemp<T*, int*> {//特化出一個T*版本,語法:類名<類型...>,區別於模板類的定義
public: T Max(T* a, T* b) { cout << "max *a *b" << endl; return *a > *b ? *a : *b; } }; template<> //全特化
class tehuaTemp<string, int> {//特化出一個字符串版本
public: int Max(string a, string b) {//字符串特化版本的Max
        cout << "stricmp" << endl; return _stricmp(a.c_str(), b.c_str()); } }; //4,模板特化相似於特化繼承,正因如此,兩者不能同時存在 //以下,CTX1與tehuaTemp<int>等價,不能同時存在
class CTX1 : public tehuaTemp<int, string> { }; //5,類型參數個數可變的模板
template<typename t1, typename... others>
void xprintx(t1 fst, others... ors) { cout << fst << endl; xprintx(ors...); } void TestVarTemplatex() { xprintx(1, 2, "hello", 4, 5, "world"); } // template<> // class tehuaTemp<int> { // 
// };

void TestTehuaTemp2() { tehuaTemp<float, int> tht; tht.Max(3, 4); //max a b

    float a = 1, b = 2; //tht.Max(&a, &b); //注意下面的調用 //1,沒有特化版本時也能夠正常調用,此時用T=float*調用原始版本 //2,有特化版本時,調用指針版本,此時類型參數爲 T=float
    tehuaTemp<float*, int*> thit; thit.Max(&a, &b); //max *a *b //指針特化版本的實際使用是字符串比較
    char* ps1 = "hello"; char* ps2 = "world"; tehuaTemp<char*, float*> scmp; scmp.Max(ps1, ps2);//max a b //另外一個字符串比較的特化版本
    tehuaTemp<string, int*> td; cout << td.Max("1", "efg") << endl; //使用字符串特化版本的Max
 } #pragma endregion
#pragma endregion

#pragma region 2018.8.7
#pragma region 變量初始化及順序
class CtestInitobj { public: CtestInitobj(float fx) { cout << "CtestInitobj-cons" << endl; } CtestInitobj() { cout << "CtestInitobj-default-cons" << endl; } }; class DefaultConstrutor { class inclass { public: inclass(string s) { cout << "inclass-cons" << endl; } }; public: int fx; //1,基本類型成員默認沒有初始化
    static int ix; //2,靜態的數據必須在類外定義一次,就算是int也不例外
    inclass oic; //3,變量初始化順序與聲明順序相同
 CtestInitobj oti; public: DefaultConstrutor(float fx, string str):oic(str) { cout << "fx: " << hex << *(int*)&this->fx << endl; //cccccccc,未初始化
        cout << "default constructor" << endl; //4,對成員對象的初始化最好是放在初始化列表中,由於放在這裏賦值,會有兩次構造函數被調用: //(1),其默認構造函數,(2),有參構造函數
        oti = CtestInitobj(1); } }; int DefaultConstrutor::ix; //全局對象不寫初始值默認爲0;
void testInitorders() { DefaultConstrutor od(1, "a"); } #pragma endregion
#pragma region sealed與final及override
//sealed 與 final本質上是同樣的,MSDN:在標準類上使用final,在ref類上使用sealed //大概可理解爲c++中使用final,c#中使用sealed //但實際情形是C++中可使用final或sealed或同時混合使用,C#中只認sealed
class Csealfinal /*final sealed*/ {//1,修飾類時,能夠一塊兒使用,或單個使用,該類不能被繼承
public: virtual void out() /*final sealed*/ {//2,修飾虛函數時,能夠一塊兒使用,或單個使用,該函數不能被override
        cout << "virtual-out-func" << endl; } void funx() /*final*/ {//3,不能修飾非虛函數
 } }; class dcsealfinal sealed //4,注意這個sealed的寫法位置
    : public Csealfinal { private: void out() override sealed { cout << "deriseal-out-" << endl; } }; #pragma endregion
#pragma region 在類外調用私有函數
class CallPrivateVirtualFunc /*final sealed*/ {//修飾類時,能夠一塊兒使用,或單個使用,該類不能被繼承
public: virtual void out() { cout << "virtual-out-func" << endl; } }; class Dcallprivate : public CallPrivateVirtualFunc { private: void out() {//注意,這是個私有的重寫虛函數,它也能實現多態調用
        cout << "deriseal-out-" << endl; } }; void testCallPrivateFuncOutside() { Dcallprivate osa; CallPrivateVirtualFunc* psa = new Dcallprivate(); //利用虛函數調用機制,能夠在類外調用私有函數
    psa->out(); //調用私有的函數
} #pragma endregion
#pragma region 靜態類
static class CStaticClass { public: CStaticClass() { cout << "cstaticclass-constructor" << endl; } static int fx; string name = "static class"; }; int CStaticClass::fx; //默認爲0
void Teststaticclass(){ //從如下測試看來,C++中的靜態類與普通類沒有任何區別
 CStaticClass os; os.name = "os"; auto* pcs = new CStaticClass(); cout << pcs->name << ", " << pcs->fx << endl; cout << CStaticClass::fx << endl; } #pragma endregion
#pragma region mutable變量在const函數中的使用
class cConstclassMutable { float fx; mutable float fy; public: void Print() const { fy = 10; //ok //fx = 10; //error
 } }; #pragma endregion
#pragma region const&返回值並不絕對安全
class CConstRefcls { char str[10] = "hello"; public: const char& getStr(int idx) { return str[idx]; } char& getStrc(int idx) const { } void out() { cout << str << endl; } }; void TestModifyCOnstref(){ CConstRefcls ocr; char* pc = (char*) &(ocr.getStr(0)); *(pc) = 'x'; ocr.out(); } #pragma endregion
#pragma region 跨編譯單元的初始化問題
//遊戲引擎設計中,須要一個gGame的全局對象來供全部地方方便的使用 //【有問題的設計】會有跨編譯單元的初始化順序問題 //game.h,實現以下,使用者可能在game.cpp編譯前已經要使用gGame了,這時候將報錯 // class CGame { // }; // extern CGame gGame; //game.cpp中實現定義 //CGame gGame; //【正確的設計】
CGame& theWorld() {//在使用前總能保證明例對象已初始化,且不用便不初始化
    static CGame sgame; //利用了局部靜態變量的性質:只會在初次使用時定義一次,且一直存在
    return sgame; } void testSingleton() { gGame.InitGame();//使用,沒問題 //但這個設計存在一個問題,如何保證gGame在全部使用者使用前已初始化完成? //在跨編譯單元時,這種順序是沒法保證的,所以,引出了另外一種設計:
 theWorld().InitGame(); } #pragma endregion 利用local static實現單例
#pragma region 一個空類有什麼
//當你寫出一個空時,編譯器會爲它生成: //public inline的默認構造函數 //public inline的拷貝構造函數 //public inline的析構函數 //public inline的operator= //class CEmpty{}; //空類
class CEmpty {//編譯器生成的類,但這些東西,只有在被須要時才產生
public: CEmpty(){} CEmpty(const CEmpty& rhs){} ~CEmpty(){} //若基類是虛析構函數,則編譯器在這裏也會生成一個默認的虛析構函數
    CEmpty& operator=(const CEmpty& rhs) { return *this; } }; void TestCompilerClass() { CEmpty od; //生成默認構造函數,析構函數
    CEmpty od1(od); //copy constructor
    od1 = od; //operator=
 } #pragma endregion
#pragma region 阻止類對象被拷貝
//方法一: 將拷貝構造函數與operator=聲明爲private且不實現,錯誤只能在連接期間被發現【見effective C++】 //實際上,對於這種狀況,現代編譯器也能在編譯期直接發現錯誤,測試版本[VS2017]
class CUncopyableCls { public: CUncopyableCls(){} ~CUncopyableCls() {} private: CUncopyableCls(const CUncopyableCls& rhs); CUncopyableCls operator=(const CUncopyableCls& rhs); }; //方法二,生成一個基類,將拷貝構造函數與operator=聲明爲private且不實現 //同時,生成一個子類,不寫拷貝構造函數和operator= //這樣,編譯器在編譯時就會嘗試爲子類生成兩者
class CUncopyable { protected://使能夠繼承
 CUncopyable() {} ~CUncopyable() {} private: CUncopyable(const CUncopyableCls& rhs); CUncopyable operator=(const CUncopyable& rhs); float fx; }; class CUncopyableClass : public CUncopyable {//不實現拷貝構造函數和operator=
 }; void testUncopyableobj() { CUncopyableCls ou1, ou2; //ou1 = ou2; //編譯錯 
 CUncopyableClass oup, oup1; //oup = oup1;//編譯錯:編譯器發現有operator=需求,嘗試爲CUncopyableClass生成operator= //並調用基類operator=,但因爲它爲私有的,不能調用,所以,編譯出錯
} #pragma endregion
#pragma region 抽象類及虛析構
//1,當聲明一個虛析構時,意味着類爲基類 //2,繼承別人的類時,要注意它有沒有虛析構,如標準庫函數string
class CMystr : public string {//string類並無虛析構函數,這樣作將有內存泄漏的風險
 }; class CMyVec : public std::vector<int> {//vector也沒有
 }; void testVirtualMemLeak() { string* pstr = new CMystr(); delete pstr; //內存泄漏
 CMystr mstr; } //3,使用純虛析構函數實現抽象基類的trick
class CMyxxxAbastractcls { float fx; string name; public: void instFunc(){} virtual ~CMyxxxAbastractcls() = 0; //這樣就聲明瞭一個純虛析構函數,有純虛函數的類即是抽象類,不能被實例化
}; //4,但必須實現它,不然連接報錯: 由於析構子類對象時最終要調用基類析構函數 //抽象類也須要被析構,由於抽象類並不像C#的接口,抽象類中有成員數據須要釋放
CMyxxxAbastractcls::~CMyxxxAbastractcls(){ cout << "pure virtual function " << endl; } class CDxxObj : public CMyxxxAbastractcls { public: CDxxObj() {} ~CDxxObj() {} }; void testpurevirtualfunc() { CMyxxxAbastractcls* pyx = new CDxxObj(); delete pyx; //pure virtual function
} #pragma endregion

#pragma endregion
#pragma region 2018.8.8
#pragma region 結構體&聯合體&字節對齊&大小端
void testEndian() { //1, 聯合體使用測試
    union  {//1.1,C風格定義並初始化
        char ch; char ch1; long l; } un = { 1 }; union ux {//1.2,C++風格定義
        char ch[4]; long l; ux(){} ux(long l) {//能夠有構造函數
            this->l = l; } void fx() {//能夠有函數,默認公有,同結構體
            cout << "union func: " << endl; cout << ch[0] << endl; } }; //1.3 聯合體無論字節對齊,只取成員中最大的尺寸
    union UN //size = 1
 { char ch; char ch1; char ch2; }; union UN1 //size = 3
 { char ch[3]; }; union UN2 //size = 4
 { char ch; int it; }; //2,結構體字節對齊
#pragma pack(push, 4)//老是按4的整數倍分配內存,寧多很多
    struct ST { char ch; //4
        double d; //8
        char ch1; //4
        char ch2; short srt; int ix;//4
        char ch3;//4
 }; #pragma pack(pop) //恢復原來對齊 cout << sizeof(ST) << endl; //3,大小端模式 //經常使用機型均爲小端模式: //(1) X86系統架構 (2) arm系列:IOS,android等,默認爲小端模式,可設置爲大端 //3.1 使用聯合體測試機型大小端
    union { char ch[4]; long l; } uni = { 0x64636261 }; cout << (uni.ch[0] == 0x61 ? "小端" : "大端") << endl; } #pragma endregion
#pragma region 舊式轉換的隱患
class cax { public: float fax = 1; }; class cbx : public cax { public: float fbx = 2; }; class ccx { public: float fcx = 3; }; void testForceCasttype() { cax* pax = new cbx(); cax* pa = new cax(); //舊式轉型在類對象中無安全保證
    cbx* pbx = (cbx*)pax; cbx* pb = (cbx*)pa; //不是子類型,運行時不報錯,直到經過pb指向內部成員
    ccx* pcx = (ccx*)pax; //沒有繼承關係編譯器不報錯,由於任何指針間均可以強轉
 cout << pb->fax << endl; //1,輸出的是fax
    cout << pb->fbx << endl; //不肯定值,由於pb並非一個cbx類型
    cout << pcx->fcx << endl; //1,輸出的是fax的值
} #pragma endregion
#pragma region STL-VECTOR
void testvector() { vector<int> vec{ 1, 2, 3 }; vector<string> vecStrs(1); vecStrs.emplace_back("hello"); cout << vecStrs.size() <<", " << vecStrs.capacity() << endl; } #pragma endregion
#pragma region 2018.8.14
#pragma region int型數值邊界
void testIntLimit() { //C++中不容許使用-2147483648這個值,由於它把符號與數值當成兩部分來看了 //首先把2147483648當作uint32,再加上前面的負號時,編譯器報錯:一元負運算符應用於無符號類型,結果仍爲無符號類型 //int32的表示範圍是 -2147483648 ~ 2147483647,C++不讓用,有點奇怪,C#是能夠的 //因上在C++中,只能這樣用: 
    int ix = -2147483647 - 1; //系統宏INT_MIN 就是這樣定義出來的
    cout << ix - 1 << endl; //0x80000000 + 0xffffffff = 0x7fffffff //C#中能夠這樣用 //int ix = -2147483648; //ok,2147483648被看做uint32 //WriteLine(ix - 1); //0x80000000 + 0xffffffff = 0x7fffffff
} #pragma endregion
#pragma region 彙編測試
void testASM() { int i = 10; int j = 20; int bp = 0; _asm { mov dword ptr[i], 10h //mov dword ptr[bp], 10h //編譯出錯,變量bp與寄存器同名,被認爲是16位的bp寄存器
 mov dword ptr[j], edi lea eax, [ebp] mov DWORD ptr[j], eax } cout << i << "," << j << endl; } #pragma endregion

#pragma endregion
#pragma endregion

void xprintx() { } int _tmain(int argc, _TCHAR* argv[]) { //TestVarTemplatex();
 TestTehuaTemp2(); //testInitorders(); //testASM(); //testIntLimit(); //testvector(); //testForceCasttype(); //testEndian(); //testpurevirtualfunc(); //testVirtualMemLeak(); //testSingleton(); //TestModifyCOnstref(); //testInitorders(); //testCallPrivateFuncOutside(); //Teststaticclass(); //TestTehuaTemp2(); //testDeleteStack(); //TestMySmartPtr(); //TestShareptrUse(); //TestThreadSync(); //TestThread(); //TestConstOverload(); //testcasttype(); //TestArrayAndPtr(); //TestSignedUnsignedAssign(); //TestPointerType(); //TestMemLayout(); //Test4Typecast(); //TestOddConstructor(); //TestXiebian(); //a = b; //EffectiveCpp::Test02(); //TestStlSortFunc(); //Dijkastra(); //TestPointerType(); //TestSameTypeAssign(); //TestRef(); //TestTehuaTemp(); //TestCComplexOper(); //TestTypecastOper(); //TestTypecastContructor(); //TestNestingFuncPtrs(); //TestArrayAndPointer();
    ///TestRealloc();
    //TestComputeDataStorage(); //TestVirtualFunctionTable(); //TestAdd2Num(); //TestAstrPstr(); //TestCWithStatic(); //TestThread(); //TestVarTemplate();

    return 0; }
相關文章
相關標籤/搜索