指针的判别:
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; }
stl = 类模板+ 函数模板
实体函数 = 函数模板 + 参数
类实体 = 类模板 + 参数
子类可以初始化父类,父类指针可以指向子类。父类不可以初始化子类,子类不可以指向父类。
函数模板/类模板 + 部分特化/完全特化 + 字面量参数模板
void* 不确定指针类型。可以转化为任意类型指针,可以接受任意类型指针。