• C++全总结


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