动态内存分配
C++中的动态内存分配
-
C++中通过new关键字进行动态内存申请
-
C++中的动态内存申请是基于类型进行的
-
delete关键字用于内存释放
(动态内存的申请和释放,使用的是关键字,说明他们是C++的一部分,不是用的库函数)
int* p = new int;
*p = 5;
*p = *p + 10;
printf("p = %0x\n", p);
printf("*p = %d\n", *p);
delete p;
result:
p = fc1780
*p = 15
int* p = new int[10];
for(int i=0; i<10; i++)
{
p[i] = i + 1;
printf("p[%d] = %d\n", i, p[i]);
}
delete[] p;//注意[],释放不干净会内存泄漏
result:
p[0] = 1
p[1] = 2
p[2] = 3
p[3] = 4
p[4] = 5
p[5] = 6
p[6] = 7
p[7] = 8
p[8] = 9
p[9] = 10
new关键字与malloc函数的区别
new关键字是C++的一部分,malloc是由C库提供的函数
new以具体类型为单位进行内存分配,malloc只能以字节为单位进行内存分配
new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特性
int* pi = new int(1);
float* pf = new float(2.0f);
char* pc = new char('c');
printf("*pi = %d\n", *pi);
printf("*pf = %f\n", *pf);
printf("*pc = %c\n", *pc);
delete pi;
delete pf;
delete pc;
result:
*pi = 1
*pf = 2.000000
*pc = c
Press any key to continue...
new关键字的初始化
-
char* pc = new char('c'); //pc指向堆空间里char类型的变量,将这个char类型的变量初始化为c
-
int* pi = new int(1); float* pf = new float(2.0f);类似
C++中的命名空间
在C语言中只有一个全局作用域
C语言中所有的全局标识符共享同一个作用域,标识符之间可能发生冲突
C++中提出了命名空间的概念
-
命名空间将全局作用域分成不同的部分
-
不同命名空间中的标识符可以同名而不会发生冲突(解决命名冲突问题)
-
命名空间可以相互嵌套
-
全局作用域也叫默认命名空间
C++命名空间的定义:
C++命名空间的使用:
-
使用整个命名空间:using namespace name;
-
使用命名空间中的变量:using name::variable;
-
使用默认命名空间中的变量:::variable
默认情况下可以直接使用默认命名空间中的所有标识符。
#include <stdio.h>
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
using namespace First;//打开了First这个命名空间
using Second::Internal::P;
//打开了Second中Internal中的P变量
printf("i = %d\n", i);
printf("i = %d\n", Second::i);
//没有打开Second全部变量,直接访问Second中的i
P p = {2, 3};//已经打开了,直接使用P
printf("p.x = %d\n", p.x);
printf("p.y = %d\n", p.y);
printf("Press any key to continue...");
getchar();
return 0;
}
result:
i = 0
i = 1
p.x = 2
p.y = 3
Press any key to continue...
加作用域是一种明确指定你想要访问哪个命名空间中的标识符的方式
强制类型转换
C方式的强制类型转换(这里作为反面教材,随意类型转换,导致出现安全问题)
/*
定义了一个新的类型别名PF。
这个新类型是一个指向函数的指针,
该函数接受一个int类型的参数并且没有返回值(返回类型为void)
*/
/*
void(PF)(int);
这是一个函数指针的声明。
它表示一个指向函数的指针,该函数接受一个int类型的参数并返回void。
*/
typedef void(PF)(int);
struct Point
{
int x;
int y;
};
int main()
{
int v = 0x12345;
PF* pf = (PF*)v;
char c = char(v);
pf(v);
Point* p = (Point*)v;
printf("p->x = %d\n", p->x);
printf("p->y = %d\n", p->y);
printf("Press any key to continue...");
getchar();
return 0;
}
result:
ops!可以通过编译,但是程序运行错误
C语言随便强制类型转换,造成了程序崩溃
C方式强制类型转换存在的问题
-
过于粗暴
-
任意类型之间都可以进行转换,编译器很难判断其正确性
-
-
难于定位
-
在源码中无法快速定位所有使用强制类型转换的语句
-
强制类型转换在实际工程中还是可能使用的
如何进行更加安全可靠的转换?
C++将强制类型转换分为4种不同的类型
Type:最终要转换的目标;
Expression:所要转换的变量的值/表达式
C++提倡用这些关键字进行强制类型转换(安全,可读性)
static_cast强制类型转换
-
用于基本类型间的转换,但不能用于 基本类型 指针间 的转换
-
用于有继承关系类对象之间的转换和类指针之间的转换
static_cast是编译期进行转换的,无法在运行时检测类型,所以类类型之间的转换可能存在风险。
const_cast强制类型转换
用于去除变量的const属性(只读变量降级到普通变量)
#include <stdio.h>
int main()
{
const int& j = 1;//常引用
int& k = const_cast<int&>(j);
const int x = 2;//常量
int& y = const_cast<int&>(x);//想把常量的const属性去掉
//去掉以后就得到一个变量了吗?
//对x取引用(地址,指针),编译器会对这个常量分配一段内存,y就是x这个常量内存地址的别名
k = 5;
printf("k = %d\n", k);
printf("j = %d\n", j);
y = 8;
printf("x = %d\n", x);//x是常量,进入到符号表,从符号表取址,此处打印2
printf("y = %d\n", y);//y被赋值为8,此处打印8
printf("&x = %p\n", &x);//x的地址和y的地址是一样的
printf("&y = %p\n", &y);//y就是x这个常量内存地址的别名
printf("Press any key to continue...");
getchar();
return 0;
}
result:
k = 5
j = 5
x = 2
y = 8
&x = 000000000061FE00
&y = 000000000061FE00
Press any key to continue...
reinterpret_cast强制类型转换
-
用于指针类型间的强制转换
-
用于整数和指针类型间的强制转换
reinterpret_cast直接从二进制位进行复制,是一种极其不安全的转换。
#include <stdio.h>
int main()
{
int i = 0;
char c = 'c';
int* pi = &i;
char* pc = &c;
//将字符变量i的地址强制转化为char类型的指针,有意义吗?
pc = reinterpret_cast<char*>(pi);
//将字符变量c的地址强制转化为int类型的指针,有意义吗?
pi = reinterpret_cast<int*>(pc);
//c = reinterpret_cast<char>(i);
// Oops! static_cast这么做的
printf("Press any key to continue...");
getchar();
return 0;
}
dynamic_cast强制类型转换
-
主要用于类层次间的转换,还可以用于类之间的交叉转换
-
dynamic_cast具有类型检查的功能,比static_cast更安全
小结
C++中内置了动态内存分配的专用关键字
C++中的动态内存分配是基于类型进行的
C++中命名空间概念用于解决名称冲突问题
C++细化了C语言中强制类型转换的方式
-
-
C++不推荐在程序中使用强制类型转换
-
C++建议在强制类型转换时考虑一下究竟希望什么样的转换
-