2018.8.14-C++复习笔记总

2018.8.14-C++复习笔记总

// CPPTEST.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include
#include 
#include
#include
#include 
#include"TMyNumOperator.h"
#include"abc.h"
#include 
#include
#include 
#include 
#include 
#include 
#include 

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 _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();//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 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 TestStrSplit(string s, char sep){
//    std::list 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 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 class MyVector{
//    std::vector _lst;
//
//public:
//
//    void Test2();
//};
//
//template void MyVector::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 <<"," <")" << 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 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()不合法
    //3,【注意这里第二个参数是个引用类型,若是AddTwoNumber对象类型则会出错,不能在类中定义本类对象】
    friend void operator+ <>(T os, AddTwoNumber& 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& n) {
    cout << "operator+: n + AddTwoNumber: " << os << endl;
}

template<class T>
void operator+(T n, A o) {
    cout << "operator+: n + A : " << n << endl;
}

//==================================================
//※※※※※※注意这种多层的模板前置声明※※※※※※※
template //类模板的前置声明
template //函数模板的前置声明
T1 AddTwoNumber::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
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& rhs) {
        operator=(rsh);
    }

    SmartPointer operator=(const SmartPointer& 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
class CTehuaTemp {
public:
    T px = "abc";//2,被特化为了一个char*类型指针,故可以这样用
};
template
class CDThhuaTemp : public CTehuaTemp {//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 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
        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 
class CAnimal {
public:
    void Print(T info) {
        cout << info << endl;
    }
};

template
class CHuman : public CAnimal {

};
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* panim = new CHuman[10]; //正确,这个还是普通的数组协变,而不是像C#那样真正的泛型协变
    //CAnimal* panim = new CAnimal[10]; //错误
    //CAnimal* panim = new CHuman[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(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(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(b); //ok
    a = static_cast(b); //ok
    //b = static_cast(a); //error,编译错误

    //2,指针类型转换,只检查有无父子关系,不进行动态类型检查
    //动态类型即运行时,指针实际指向的类型
    CB* pb = static_cast(&a); //ok,但运行时访问CB类(不属于CA)的部分将出错
    pb = (CB*)&a; //等价于上式
    cout << pb->fbx << endl; //输出一个未初始化的或不可预料的值
    CA* pa = new CB();
    pb = static_cast(pa);
    cout << pb->fbx << endl;  //ok

    //------------------------------------------------------------------
    //二,动态类型转换 dynamic_cast,【用于指针或引用】
    //【基类必须定义了虚函数,说明动态时类型检查是根据虚函数表来做的】
    pb = dynamic_cast(&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 <"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(&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] = ∾ //指向具有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 ptr(new managedOjbect);
    std::shared_ptr ptr1(ptr); //ok, ptr1与ptr引用同一个对象
    ptr->fx = 10;
    ptr1 = ptr;
    ptr = ptr1;

    std::shared_ptr ptr2(); //这是函数声明式,不是指针!!!

                                           //2,不要将一个裸指针同时托管给多个智能指针,否则将发生重复析构
    auto* pobj = new managedOjbect();
    std::shared_ptr ptr3(pobj);
    //std::shared_ptr ptr4(pobj); //析构异常,重复delete

    auto* pobj1 = new managedOjbect();
    auto* pobj2 = new managedOjbect();
    std::shared_ptr ptr5(pobj1);

    //3,尽量不要使用reset,因为它会释放托管对象,会导致其它智能指针异常
    ptr5.reset(); //立即释放托管堆,即delete pobj1
    ptr5.reset(new managedOjbect()); //立即释放原来的,持有新的对象

                                     //4,智能指针不能用于数组
                                     //std::shared_ptr arrptr(new managedOjbect[10]); //析构时异常,原因见下面异常分析
                                     //std::shared_ptr 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
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 ptr(new managedOjbect);
    ptr.name = "ptr";
    SmartPtrl ptr2(ptr);
    ptr2.name = "ptr2";

    //自赋值及相等指针赋值测试
    ptr2 = ptr;
    ptr = ptr2;
    ptr = ptr;

    SmartPtrl ptr3(new managedOjbect);
    ptr3.name = "ptr3";

    SmartPtrl 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,查看内存头,数据似乎也正常
    CHeapDebugger1>::Print(pobj);

    //总结:
    //对于基本类型,使用 delete[]万无一失
    //对于类对象类型,单对象使用delete,数组对象使用delete[],否则出错
    //对单对象使用delete[],将出现上面的死循环析构
    //对数组对象使用delete,将抛出运行时异常

}
#pragma endregion
#pragma region 模板特化
//------------------------------------------------------------------------------------------
//模板特化的起因是:某些特定类型不适用于模板的通用算法,需要特化出一个专用版本来处理它(写个特定算法)
//同时,由于模板不支持定义类名相同而类型个数不同的模板
//对于模板类型参数个数可变的需求,C++11提供了typename...语法支持可变类型参数
//------------------------------------------------------------------------------------------
//1,模板特化是指在原始模板的基础上,特化出一个同名的模板来,只是参数被特化了
//注意模板特化的语法是在类名后加<>并指定特化类型
template
class tehuaTemp {//原始版本
public:
    T Max(T a, T b) {
        cout << "max a b" << endl;
        return a > b ? a : b;
    }
};

//2,只能定义一个模板原始版本,不能通过增加或减少模板类型参数个数来定义不同的同名模板
// template //不能这样做,
// class tehuaTemp{
// public:
//     T Max(T a, T b) {
//         cout << "max a b" << endl;
//         return a > b ? a : b;
//     }
// };

//3,这不是模板特化
// template
// class tehuaTemp {//原始版本
// public:
//     T Max(T a, T b) {
//         cout << "max a b" << endl;
//         return a > b ? a : b;
//     }
// };
template//偏特化
class tehuaTempint*> {//特化出一个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等价,不能同时存在
class CTX1 : public tehuaTemp<int, string> {

};
//5,类型参数个数可变的模板
template
void xprintx(t1 fst, others... ors) {
    cout << fst << endl;
    xprintx(ors...);
}
void TestVarTemplatex() {
    xprintx(1, 2, "hello", 4, 5, "world");
}
// template<>
// class tehuaTemp {
// 
// };

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;
}

 

posted on 2018-08-14 11:20 时空观察者9号 阅读(...) 评论(...) 编辑 收藏

你可能感兴趣的:(2018.8.14-C++复习笔记总)