指针的判别:

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* 不确定指针类型。可以转化为任意类型指针,可以接受任意类型指针。

原文地址:https://www.cnblogs.com/zsy12138/p/10871601.html