C++-(三)动态内存分配:new / delete,名字空间:namespace,新型的类型转换:xxx_cast

(一)C++中的动态内存分配

  • 对比C和C++中的动态内存分配
    在C语言中,动态内存分配是通过库函数malloc()来申请完成的,但是C语法本身并没有动态内存分配的功能。因为C语言是一种偏底层的语言,所以在一些底层硬件平台的编译器上是不支持动态内存分配的,因此也不支持malloc()库函数。

(1)动态内存分配:new / delete

在C++中的动态内存分配,new / delete关键字是C++语法本身所支持的语言,所以不管是在任何的编译器上面还是在任何硬件平台上面,C++都能够进行动态内存分配。

  • new / delete 的使用:
    在这里插入图片描述
    注意:
    (1)在一个数组中,
    delete[] p:表示释放数组的整个内存空间;
    delete p:仅释放数组中第一个元素的内存空间,剩下元素的内存空间没有被释放,将造成内存泄漏。
    (2)delete之后的指针p要指向NULL:p = NULL;,否则会出现野指针。

(2)malloc()和new 申请堆内存空间的区别

  • C语言中动态内存分配,malloc()是以字节为单位进行内存分配,传入整型参数指定申请内存空间的大小。malloc在申请堆内存空间时,不具备内存初始化的特性。
  • C++是强类型的语言,在C++动态内存申请时,new以具体类型为单位进行内存分配,delete用于内存释放。new在申请单个类型变量的堆内存时,可以进行初始化赋值。
    eg:
    int * p = new int(3):指针p指向一个堆内存空间为int类型的变量,new申请成功后将该内存空间初始化为3。

(二)命令空间/名字空间

在c语言中,有全局作用域和局部作用域。但是C语言中全局作用域只有一个,而且所有的全局标识符共享同一作用域,因此可能会造成全局标识符重名的冲突。于是C++提出了名字空间的概念。

  • 名字空间
    名字空间将全局作用域分成不同的部分,因此在不同名字空间中的全局变量标识符可以同名,且不会发生冲突。全局作用域也称默认名字空间,同时名字空间内部也可以相互嵌套。

(1)名字空间的定义:namespace

namespace关键字,语法格式如下:

namespace Global
{
	namespace Inter
	{
		/* ...*/
	}
	/* ...*/
}

注意:无论怎么划分全局作用域,不同名字空间里面的变量/函数本质上还都是全局变量/全局函数,都还是属于全局作用域的范围。

(2)名字空间的使用:using ::

  • 使用整个名字空间声明:using namespace Name(类class);
    声明后就可以使用类定义的整个名字空间中的全局变量和全局函数
  • 使用名字空间中的变量 / 标识符using Name::variable / identify;
    using Name::可以使用变量或者定义新类型结构体的标识符等
  • 使用默认名字空间:::variable / identify

代码解释:

	#include <stdio.h>
	
	namespace First
	{
	    int i = 0;
	}
	
	namespace Second
	{
	    int i = 1;
	    
	    namespace Internal
	    {
	        struct P//定义了P结构体类型
	        {
	            int x;
	            int y;
	        };
	    }
	}
	
	int main()
	{
	    using namespace First;//使用名字空间First内所有标识符
	    using Second::Internal::P;
	    
	    printf("First::i = %d\n", i);
	    printf("Second::i = %d\n", Second::i);
	    
	    P p = {2, 3};//定义了一个P类型结构体变量p并初始化
	    
	    printf("p.x = %d\n", p.x);
	    printf("p.y = %d\n", p.y);
	    
	    return 0;
	}

(三)新型的类型转换

C语言中,强制类型转换所存在的问题如下:

	#include <stdio.h>
	
	typedef void(PF)(int);
	
	struct Point
	{
	    int x;
	    int y;
	};
	
	int main()
	{
	    int v = 0x12345;
	    PF* pf = (PF*)v;
	    char c = char(v);
	    Point* p = (Point*)v;
	    
	    pf(5);
	    
	    printf("p->x = %d\n", p->x);
	    printf("p->y = %d\n", p->y);
	 	printf("c = %c\n", c);

	    return 0;
	}

运行结果:能编译通过,但是出现段错误

linux@ubuntu:~/test$ g++ demo.cpp -Wall
linux@ubuntu:~/test$ ./a.out 
Segmentation fault (core dumped)

(1)C语言中强制类型转换的问题

  • 过于粗暴
    任意类型之间都可以进行类型转换,C编译器难以判断其正确性,没法编译时直接指出语法错误。

  • 难以定位
    在源码中,无法快速定位所有强制类型转换的语句。

  • C语言中强制类型转换写法:

int a = 0x123;
char ch = (int)a; 
//char ch = char(a);int * p = (int*)&a;
//int * p = (int*)(&a);

在实际工程应用中,强制类型转换是很难完全避免的,那么我们如何能更加安全可靠的使用转换呢?
于是C++便提出新式类型转换:对强制类型转换进行功能的划分。

(2)C++4种强转的使用条件

C++强转使用4种关键字,完成4种类型的转换。
通过搜索强制类型转换定义时的关键字,可以快速定位相应的强制类型转换语句。

(1)4种类型的适用条件:记住每条使用规则,可以减少大型工程中代码bug

  • static_cast(静态类型转换)
    1)主要用于基本类型间的类型转换
    2)不能用于基本类型指针间的类型转换
    3)用于有继承关系类对象之间的转换和类指针之间的转换

  • const_cast
    1)用于去除变量的只读属性ro(readonly)
    2)强转的目标类型必须是指针或引用

  • dynamic_cast(动态类型转换)
    1)用于有继承关系或交叉关系类指针间的转换;(也是只能用于指针间的转换)
    2)具有类型检查的功能;(如果强转不成功,则dynamic_cast返回空指针NULL)
    3)定义的类里面必须有虚函数的支持

  • reinterpret_cast(重解释转换)
    1)用于指针类型间的强转
    2)用于**整数和指针类型间**的强转(eg:整型数值强转成地址addr)

(2)用法:xxx_cast<强转类型>(变量/表达式);

(3)代码举例

	#include <stdio.h>
	
	void static_cast_demo()
	{
	    int i = 0x12345;
	    char c = 'c';
	    int* pi = &i;
	    char* pc = &c;
	    
	    c = static_cast<char>(i);
	    pc = static_cast<char*>(pi); //错误:static_cast不能用于基础类型指针间
	}
	
	void const_cast_demo()
	{
	    const int& j = 1;//正确:定义了一个只读变量j
	    int& k = const_cast<int&>(j);//const_cast将只读变量j的只读属性去掉,j即变成一个普通的变量
	    							//引用k就可以修改j的值了
	    							
	    const int x = 2;//正确:定义了一个真正意义上的常量x,编译器将初始值2和常量x放入符号表。
	    				//C++编译器会对const修饰的变量分配内存空间,平时不单独使用。
	    int& y = const_cast<int&>(x);//此时y代表只读变量,y指向的内存空间是编译器为常量x分配内存
	    					//空间,空间起别名为y,通过只读变量y可以使用常量x的内存空间。
	    					//当使用常量x时,x的值从符号表中取值。
	    					//使用或改变y的值:实际更改的是常量x分配的内存空间中的值。
	    					//const_cast去除x的只读属性,其实去除的是常量x内存空间的只读属性。
	    
	    int z = const_cast<int>(x);//错误,const_cast只能用于指针/引用,不可用于基本类型间。
	    
	    k = 5;
	    
	    printf("k = %d\n", k);
	    printf("j = %d\n", j);
	    
	    y = 8;
	    
	    printf("x = %d\n", x);
	    printf("y = %d\n", y);
	    printf("&x = %p\n", &x);
	    printf("&y = %p\n", &y);
	}
	
	void reinterpret_cast_demo()
	{
	    int i = 0;
	    char c = 'c';
	    int* pi = &i;
	    char* pc = &c;
	    
	    pc = reinterpret_cast<char*>(pi);//正确:可用于指针间转换,指针和整型数值地址强转
	    pi = reinterpret_cast<int*>(pc);//正确
	    pi = reinterpret_cast<int*>(i);//正确:可以用于整形和指针间
	    c = reinterpret_cast<char>(i); //错误:reinterpret不能用于基本类型间
	}
	
	void dynamic_cast_demo()
	{
	    int i = 0;
	    int* pi = &i;
	    char* pc = dynamic_cast<char*>(pi);//错误:没有虚函数的支持
	}
	
	int main()
	{
	    static_cast_demo();
	    const_cast_demo();
	    reinterpret_cast_demo();
	    dynamic_cast_demo();
	    
	    return 0;
	}

注释掉出错代码后,运行结果:

linux@ubuntu:~/test$ g++ demo.cpp
linux@ubuntu:~/test$ ./a.out 
k = 5
j = 5
x = 2
y = 8
&x = 0xbfb77a80
&y = 0xbfb77a80
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值