zoukankan      html  css  js  c++  java
  • 指针的判别:

    C++支持C语言的可变参数函数。

    C++编译器的相同函数名时函数调用的优先级:1. 重载函数 2. 函数模板 3. 变参函数

    指针判别思路:根据重载规则进行参数判别

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Test
    {
    public:
        Test()
        {  }
        virtual ~Test()
        {  }
    };
    
    template
    <typename T>
    char IsPtr(T* v) 
    {
        return 'd';
    }
    
    int IsPtr(...)  
    {
        return 0;
    }
    
    #define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))  // 编译器期间就确定要调用的函数,通过函数返回值大小判断参数类型,sizeof()并不会调用函数,避免了错误指令
    
    int main(int argc, char *argv[])
    {
        int i = 0;
        int* p = &i;
        
        cout << "p is a pointer: " << ISPTR(p) << endl;    // true
        cout << "i is a pointer: " << ISPTR(i) << endl;    // false
        
        Test t;
        Test* pt = &t;
        
        cout << "pt is a pointer: " << ISPTR(pt) << endl;    // true
        cout << "t is a pointer: " << ISPTR(t) << endl;    // false
        cout << "t is a pointer: " << IsPtr(t) << endl;    // 指令错误,由于C语言的变参宏无法处理类  
        return 0;
    }

    构造函数抛出异常:

    构造函数立即停止,对象无法生成,析构函数不会被调用,对象所占空间立即被收回。

    在构造函数可能发生异常时,使用二阶构造。

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Test
    {
    public:
        Test()
        {
            cout << "Test()" << endl;
            throw 0;
        }
        virtual ~Test()
        {
            cout << "~Test()" << endl;
        }
    };
    
    int main(int argc, char *argv[])
    {
        Test* p = reinterpret_cast<Test*>(8);    
        try
        {
            p = new Test();  // 程序直接从throw跳到try,不执行这条语句
        }
        catch(...)
        {
            cout << "Exception..." << endl;
        }  
        cout << "p = " << p << endl; //0x8 
        return 0;
    }

     析构函数抛出异常:对象使用资源无法完全释放,不要在析构函数扔出异常。(g++编译器中不会捕获析构函数抛出的异常)

    #include <iostream>
    #include <cstdlib>
    #include <exception>
    
    using namespace std;
    
    void my_terminate() // 异常处理函数,进行进程资源的释放,只可被调用一次
    {
        cout << "my_terminate()" << endl;
      //exit(1);  //释放空间,调用析构函数,析构中又扔出异常,又调用my_terminate
        abort();  //推荐使用abort,强制结束进程
    }
    
    class Test 
    {
    public:
        Test() 
        {
            cout << "Test()"; 
            cout << endl;
        }
        ~Test() 
        {
            cout << "~Test()"; 
            cout << endl;
            throw 2;   // 析构函数不可抛出异常,可能导致terminate多次被调用,导致多次释放同一进程资源,使得系统不稳定
        }
    };
    
    int main()
    {
        set_terminate(my_terminate);
        static Test t;
        throw 1;  // 调用my_terminate
        return 0;
    }

      

     class与typename:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template < class T >  //class在这里的功能和typename相同
    class Test
    {
    public:
        Test(T t) 
        {  }
    };
    
    template < class T >  //class在这里的功能和typename相同
    void func(T a[], int len)
    {
    
    }

    typename与class的区别:

    int a = 0;
    
    class Test_1
    {
    public:
        static const int TS = 1;
    };
    
    class Test_2
    {
    public:
        struct TS
        {
            int value;
        };
    };
    
    template
    < class T >
    void test_class()
    {
        T::TS * a;          // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式)
                            // 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作
                            // 编译器默认认为 T::TS 是成员变量
        typename T::TS * a; // 采用typename说明后面的标识符 T::TS 是一个类型,而不是成员变量           
    }
    
    int main(int argc, char *argv[])
    {
        test_class<Test_1>();  // 乘法操作
        test_class<Test_2>();  // 定义指针变量
        return 0;
    }

    typename的作用:

      1. 在模板定义中声明泛指类型

      2. 明确告诉编译器其后的标识符是类型

    try和catch和throw:

    1. try和catch本意是分隔正常代码和异常代码,可以将函数体分为两个部分。

    2. 函数声明和定义是也可以直接指定函数可能抛出的异常类型。

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int func(int i) throw(int,char) // 函数异常声明,表示当前函数可能抛出异常,抛出的异常类型可以为int和char
    {                               // 声明函数异常后,抛出其他异常函数停止运行,
                                    // 通过异常声明可以定义无异常函数
        throw 0; // 这里可以抛出整型和字符型异常。
    }
    
    void test(int i) try   // 将函数分为两个部分,上面部分是正常代码
    {
        func(i);
    }
    catch(int i)           // 下面是异常代码
    {
        
    }
    catch(...)
    {
    
    }
    
    int main(int argc, char *argv[])
    {
        test(5);  
        return 0;
    }

    const成员函数只能被const对象调用,const成员函数不能改变成员变量值,非const对象可以调用const成员函数,重载的非const成员函数会调用非const成员函数。const对象本意就是内部状态不发生改变。

    关键字:mutable

    mutable成员变量永远处于可以改变的状态

    如何改变const对象的成员变量:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class pointer_test
    {
        int * const mem;
    public:
        pointer_test(int value = 0) : mem(new int(0))  // 让成员变量为一指针,指向堆空间,
        {                                                    
            m_value = value;
        }
        int member_plus() const
        {
            *mem = *mem + 1;  // 改变时改变对应的堆空间的值
            return m_value;
        } 
        ~pointer_test()
        {
            delete mem;
        }
    };
    
    class mutable_test
    {
        mutable int mem;   // 定义成员变量为mutable,
    public:
        mutable_test(int value = 0) 
        {
            mem = 0; 
        }
        int member_plus() const
        {
            mem++; //让其在对象为const时其成员变量也是可以改变的
            return m_value;
        }
        ~mutable_test()
        {
            delete m_pCount;
        }
    };
    
    int main(int argc, char *argv[])
    {
        const mutable_test m_t;   //定义一个只读类,其内部状态不可变
        const pointer_test p_t;
        m_t.member_plus;
        p_t.member_plus;
        return 0;
    }

    静态存储区创建动态对象

    new/delet是操作符,可以全局重载(不推荐)局部重载(针对具体类型进行重载),默认分配空间是堆空间,重载可以改变申请的空间为静态存储区 。

    用new在全局区申请空间:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Test
    {
        static const unsigned int COUNT = 4; //缓冲池有4个test
        static char c_buffer[];  // 静态缓冲池大小
        static char c_map[];  // 标记在c_buffer中test块的位置
        
        int m_value;
    public:
        void* operator new (unsigned int size)
        {
            void* ret = NULL;
            
            for(int i=0; i<COUNT; i++)
            {
                if( !c_map[i] )    // 找到c_buffer中没有使用的Test块
                {
                    c_map[i] = 1;  // 将找到的test块标记为1表示已使用               
                    ret = c_buffer + i * sizeof(Test); // 返回c_buffer中空的test块起始地址                       
                    break;
                }
            }
            return ret;
        }
        
        void operator delete (void* p)
        {
            if( p != NULL )   // 保证释放的地址不为空
            {
                char* mem = reinterpret_cast<char*>(p);
                int index = (mem - c_buffer) / sizeof(Test); // 寻找释放test块在c_buffer中的位置
                int flag = (mem - c_buffer) % sizeof(Test);  // 保证指针指向每个test的起始位置
                
                if( (flag == 0) && (0 <= index) && (index < COUNT) )
                {
                    c_map[index] = 0;              
                }
            }
        }
    };
    
    char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0};   
    char Test::c_map[Test::COUNT] = {0};  
    
    int main(int argc, char *argv[])
    {
        Test* pa[5] = {0};  
        for(int i=0; i<5; i++)   // 申请五个test,最后一个会申请失败
        {                        // 使得test类最多可以申请4个对象
            pa[i] = new Test;
        } 
        for(int i=0; i<5; i++)
        {    
            delete pa[i];
        }
        
        return 0;
    }

    指定空间(堆,栈,全局区)中创建对象:

    #include <iostream>
    #include <string>
    #include <cstdlib>
    
    using namespace std;
    
    class Test
    {
        static unsigned int c_count;  // 申请对象的个数
        static char* c_buffer;  // 申请空间的起始地址
        static char* c_map;
        
        int m_value;
    public:
        static bool SetMemorySource(char* memory, unsigned int size)  // 从地址为memory大小为size的空间中分配对象的空间
        {
            bool ret = false;        
            c_count = size / sizeof(Test);     
            ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char))))); // 用calloc创建一个标记数组,大小为memory中可分配test个数
            
            if( ret )
            {
                c_buffer = memory;
            }
            else
            {
                free(c_map);  
                c_map = NULL;
                c_buffer = NULL;
                c_count = 0;
            }     
            return ret;
        }
        
        void* operator new (unsigned int size)
        {
            void* ret = NULL;      
            if( c_count > 0 )
            {
                for(int i=0; i<c_count; i++)
                {
                    if( !c_map[i] )
                    {
                        c_map[i] = 1;                  
                        ret = c_buffer + i * sizeof(Test);                                 
                        break;
                    }
                }
            }
            else
            {
                ret = malloc(size);
            }
            
            return ret;
        }
        
        void operator delete (void* p)
        {
            if( p != NULL )
            {
                if( c_count > 0 )
                {
                    char* mem = reinterpret_cast<char*>(p);
                    int index = (mem - c_buffer) / sizeof(Test);
                    int flag = (mem - c_buffer) % sizeof(Test);
                    
                    if( (flag == 0) && (0 <= index) && (index < c_count) )
                    {
                        c_map[index] = 0;
                    }
                }
                else
                {
                    free(p);
                }
            }
        }
    };
    
    unsigned int Test::c_count = 0;
    char* Test::c_buffer = NULL;
    char* Test::c_map = NULL;
    
    int main(int argc, char *argv[])
    {
        char buffer[12] = {0};   // 通过重载在C++任意位置创建对象,此时是堆空间  
        Test::SetMemorySource(buffer, sizeof(buffer));
        
        Test* pa[5] = {0};    
        for(int i=0; i<5; i++)
        {
            pa[i] = new Test;
        } 
        for(int i=0; i<5; i++)
        {  
            delete pa[i];
        }
        return 0;
    }

    new[]与delet[]的重载:

    通过重载可以改变内存管理方式。

    new[]实际返回的空间比预定要多,原因是对象数组中还包含保存数组的信息(用于确定析构函数和构造函数的调用次数)

    动态内存申请结果:

    malloc函数在申请内存失败时返回空指针NULL。
    new在申请内存失败时 前期编译器返回NULL值,现代编译器返回std:bad_alloc异常对象。

    new在分配内存时:如果空间不足,会调用全局函数new_handler()函数,new_handler()会抛出std::bad_alloc异常。可以自定义new_handler()函数来处理内存分配失败的情况

    解决方案:

      1. 全局范围:重定义new/delet的实现,不抛出异常。自定义new_handler()函数,不抛出异常。

      2. 类层次范围:根据一个类的层次重载new/delet,不抛出异常。

      3. 单次动态内存分配:使用nothrow参数,指明当前的new不抛出异常。

    自定义new_handler()函数:

      1. 通过set_new_handler(my_new_handler)设置自己的new_handler()函数。

    #include <iostream>
    #include <new>
    #include <cstdlib>
    #include <exception>
    
    using namespace std;
    
    class Test
    {
        int m_value;
    public:
        Test()
        {
            m_value = 0;
        }
        
        ~Test()
        {
        }
        
        void* operator new (unsigned int size) throw()  // 声明可能抛出任何异常
        {       
            // return malloc(size);   // 申请内存失败        
            return NULL;
        }
        
        void operator delete (void* p)
        {
            cout << "operator delete: " << p << endl;
            
            free(p);
        }
        
        void* operator new[] (unsigned int size) throw()
        {
            // return malloc(size); // 申请内存失败      
            return NULL;
        }
        
        void operator delete[] (void* p)
        {     
            free(p);
        }
    };
    
    void my_new_handler()  // 自定义实现new_handler,用来处理内存分配失败的情况
    {
            
    }
    
    void ex_func_1()
    {
        new_handler func = set_new_handler(my_new_handler); // 设置自定义函数my_new_handler为新的默认new_handler函数,
                                                            // 并且将原来默认的new_handler()函数地址返回到func.            
        try
        {
            if( func )  // 判断默认情况下是否有new_handler()函数
            {            // vc++,g++编译器没有设置new_handler()函数
                func(); // bcc编译器设置了new_handler()函数
            }
        }
        catch(const bad_alloc&)
        {
        }
    }
    
    void ex_func_2()
    {
        Test* pt = new Test();
        cout << "pt = " << pt << endl;
        delete pt;
    }
    
    void ex_func_3()
    {
        int* p = new(nothrow) int[10];   // 使用nothrow关键字声明不要抛出任何异常,只返回空指针
        delete[] p; 
        
        int bb[2] = {0};   
        struct ST
        {
            int x;
            int y;
        };   
        ST* pt = new(bb) ST();   // 在指定内存空间 bb中创建一个对象   
        pt->x = 1;
        pt->y = 2;    
        cout << bb[0] << endl;  // 1
        cout << bb[1] << endl;  // 2   
        pt->~ST();    // 在指定空间创建对象时必须显示的手动调用构造函数
    }
    
    int main(int argc, char *argv[])
    {
        ex_func_1();
        ex_func_2();
        ex_func_3();
        
        return 0;
    }
    View Code

    stl = 类模板+ 函数模板

    实体函数 = 函数模板 + 参数

    类实体 = 类模板 + 参数

    子类可以初始化父类,父类指针可以指向子类。父类不可以初始化子类,子类不可以指向父类。

    函数模板/类模板 + 部分特化/完全特化 + 字面量参数模板

    void* 不确定指针类型。可以转化为任意类型指针,可以接受任意类型指针。

  • 相关阅读:
    谈谈IE条件注释
    0916 编程实验一 词法分析程序
    C语言文法的理解
    复利究极算法
    0106递归下降语意分析
    0309 复利计算
    关于语法树和文法的评价
    10.22 词法分析程序实验心得
    0909 编译原理
    0302 关于就业方面的一些感想
  • 原文地址:https://www.cnblogs.com/zsy12138/p/10871601.html
Copyright © 2011-2022 走看看