首页 > 代码库 > C++学习笔记(十一):void*指针、类型转换和动态内存分配
C++学习笔记(十一):void*指针、类型转换和动态内存分配
void*指针
void关键字表示“空类型”的概念。但是,这里的“空类型”不表示“任意类型”,而是表示不存在的意思,也就是说C/C++不允许你写语句void a,不存在类型为void的东西.
void*表示“空类型指针”,与void不同,void*表示“任意类型的指针”或表示“该指针与一地址值相关,但是不清楚在此地址上的对象的类型”。
类型转换
C风格转换:
1 int i;2 double d;3 4 i = (int) d;5 //或6 i = int (d);
C风格转换在C++中是适用的。但是C++也提供了4种转换方法。
那为什么还需要一个新的C++类型的强制转换呢?
新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。C++中风格是static_cast<type>(content)。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。
C++转换:
static_cast(exp)
用于相关类型之间的转换,诸如:在同一个类的继承层次关系中,向上或向下转换;枚举类型与整数类型之间的转换;浮点类型与指数类型之间的转换。
static_cast它能在内置的数据类型间互相转换,对于类只能在有联系的指针类型间进行转换。可以在继承体系中把指针转换来、转换去,但是不能转换成继承体系外的一种类型
1 class A { ... }; 2 class B { ... }; 3 class D : public B { ... }; 4 void f(B* pb, D* pd) 5 { 6 D* pd2 = static_cast<D*>(pb); // 不安全, pb可能只是B的指针 7 B* pb2 = static_cast<B*>(pd); // 安全的 8 A* pa2 = static_cast<A*>(pb); //错误A与B没有继承关系 9 ...10 }
reinterpret_cast(exp)
字面理解即re-interpret,重新解析(释)的意思。故名思意,它主要用于不相关类型之间的转换,好一个英文单词在不同的上下文中,词性和词义可能完全不同。它为不同类型之间转换带来的便利,但是也伴随着风险的,如将一个十六进制整数转换为内存地址(由int-->指针类型,这两种类型完全不关联)。既然是用于不相关类型之间的转换,也就意味着编译器不会做太多的确认和承诺。
reinterpret_cast方式还有一个特点就是:目标和原始值之间至少有相同的位数,我们可以将转换之后的值再转换回去,而不像其它3种类型可能会导致精度丢失。
dynamic_cast(exp)
一种运行时(run-time)检测的类型转换,因此转换可能需要较大的运行时代价,这种类型也是用C-style是无法实现的。主要用于执行类型向下转换和继承之间的转换。
dynamic_cast 仅能应用于指针或者引用,不支持内置数据类型
表达式dynamic_cast<T*>(a) 将a值转换为类型为T的对象指针。如果类型T不是a的某个基类型,该操作将返回一个空指针。
它不仅仅像static_cast那样,检查转换前后的两个指针是否属于同一个继承树,它还要检查被指针引用的对象的实际类型,确定转换是否可行。
const_cast(exp)
用于消除变量的const限定,转换之后的变量就不再具有“const”了,如果是一个const指针的话,转换之后可以改变指向而指向其它对象。
说明:
通常情况下dynamic_cast最好些,它检查的更严格些,其次是static_cast,而后两者也就是const_cast和reinterpret_cast较之前两者貌似不太常用,而且也不推荐使用,const_cast在用于去除const的地方还是有所发挥的,reinterpret_cast在转换时,不会在内存中进行补足比特位(例如int转换到double,需要补足4字节),这往往是不安全的,而且代码也是不可移植的。
动态内存分配
一般来说,编译器将内存分为三部分:静态存储区域、栈、堆。静态存储区主要保存全局变量和静态变量,栈存储调用函数相关的变量、地址等,堆存储动态生成的变量。 在c中是指由malloc,free运算产生释放的存储空间,在c++中就是指new和delete运算符作用的存储区域。
对象创建的两种方式:
类示例:
1 #include <iostream> 2 using namespace std; 3 4 class TestNew 5 { 6 private: 7 int ID; 8 public: 9 TestNew(int ID); 10 ~TestNew(); 11 }; 12 13 TestNew::TestNew(int ID) 14 { 15 this->ID = ID; 16 } 17 18 TestNew::~TestNew() 19 { 20 std::cout<<"对象 "<<this->ID<<" 执行析构函数"<<std::endl; 21 }
方法一:
ClassName object(param);
1 TestNew test(1);
这样就声明了一个ClassName类型的object对象,C++会为它分配足够的存放对象所有成员的存储空间。
这种方法创建的对象,内存分配是分配到栈中的,由C++缺省创建和撤销,自动调用构造函数和析构函数。
该方法创建的对象调用类方法时,必须用“.”,而不能用“->”。
在函数中使用该方法时,函数内局部变量的存储单元都在栈上创建,函数执行结束后在将这些局部变量的内存空间回收。
在栈上分配内存空间效率很高,但是分配的内存容量有限。
方法二:
ClassName *object=new ClassName(param);
delete object;
1 TestNew *pTest = new TestNew(1);2 delete pTest;
C++用new创建对象时返回的是一个对象指针,object指向一个ClassName的对象,C++分配给object的仅仅是存放指针值的空间。
用new 动态创建的对象必须用delete来撤销该对象。只有delete对象才会调用其析构函数。
new创建的对象不是用“*”或“.”来访问该对象的成员函数的,而是用运算符“->”;
new创建类对象特点:
- new创建类对象需要指针接收,一处初始化,多处使用
- new创建类对象使用完需delete销毁
- new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
- new对象指针用途广泛,比如作为函数返回值、函数参数等
C++学习笔记(十一):void*指针、类型转换和动态内存分配