首页 > 代码库 > 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创建类对象特点:

  1. new创建类对象需要指针接收,一处初始化,多处使用
  2. new创建类对象使用完需delete销毁
  3. new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
  4. new对象指针用途广泛,比如作为函数返回值、函数参数等

C++学习笔记(十一):void*指针、类型转换和动态内存分配