C++中的新成员
(一)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