动态内存分配
C++中的动态内存分配
--C++中通过new关键字进行动态内存申请
--C++中的动态内存申请是基于类型进行的
--delete关键字用于内存释放
变量申请:
Type* pointer = new Type;
// ......
delete pointer;
数组申请:
Type* pointer = new Type[N];
// ......
delete[] pointer;
例:
#include <stdio.h>
int main(int argc, char *argv[])
{
int* p = new int; //p指向堆空间一个int类型变量
*p = 5;
*p = *p + 10;
printf("p = %p\n",p);
printf("*p = %d\n",*p);
delete p; //释放p指向的单个变量
p = new int[10]; //堆上动态申请一有10个int类型空间的个数组
for(int i=0; i<10 ;i++)
{
p[i] = i + 1;
printf("p[%d] = %d\n",i,p[i]);
}
delete[] p; //释放p所指向的数组,如果此处为delete p,C++编译器会认为p指向的是一个单个元素,从而导致9个int空间内存泄露,所以一定要加上数组标示符[]
......
}
new关键字与malloc函数的区别
--new关键字是C++的一部分,malloc是由C库提供的函数
--malloc不属于C语言,而new关键字属于C++,C++兼容为C语言,C++里面还可以调用malloc函数
--new具体类型为单位进行内存分配,malloc只能以字节为单位进行内存分配
--new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特性
例:
int* pi = new int(1); //用1对pi指向的new int出来的内存空间进行初始化
float* pf = new float(2.0f); //用2.0f对pf指向的申请的float变量进行初始化
char* pc = new char('c'); //用'c'对pc指向的申请的char类型变量进行初始化
printf("*pi = %d\n",*pi); //输出1
printf("*pf = %f\n",*pf); //输出2.000000
printf("*pc = %c\n",*pc); //输出c
delete pi;
delete pf;
delete pc;
C++中的命名空间
在C语言中只有一个全局作用域
--C语言中所有的全局标示符共享同一个作用域,标示符之间可能发生冲突
--C++中提出了命名空间的概念
——命名空间将全局作用域分成不同的部分
——不同命名空间中的标示符可以同名而不会发生冲突
——命名空间可以相互嵌套
——全局作用域也叫默认命名空间
C++命名空间的定义:
namespace name {/* ... *//}
例:
#include <stdio.h>
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main(int argc, char *argv[])
{
......
} //连个同名全局变量i,因为他们位于不同的命名空间里面,没有发生冲突,C++编译器看到他们位于不同命名空间后,就认为肯定是可以区分他们的,所以编译通过
C++命名空间的使用:
—使用整个命名空间:using namespace name;
—使用命名空间中的变量:using name::varible;
—使用默认命名空间中的变量:::variable
默认情况下可直接使用默认命名空间中的所有标示符
例:
#include <stdio.h>
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main(int argc, char *argv[])
{
using namespace First; //使用First这个命名空间里面的所有标示符,可以直接访问
using Second::Internal::P; //使用Second这个命名空间里面内嵌的Interal这个命名空间里面的P标示符
printf("i = %d\n",i); //此处i是First里面的i,因为First被完全打开,其中变量可直接访问 ,输出0
printf("i = %d\n",Second::i); //访问Second空间里面的i,由于Second没有被打开,里面的变量没有被完全释放,所以必须指定的访问里面的某个变量才行,输出1
P p = {2,3}; //P已经释放出来,可直接访问
printf("p.x = %d\n",p.x); //输出2
printf("p.y = %d\n",p.y); //输出3
......
}
强制类型转换
C方式的强制类型转换
(Type)(Expression) or Type(Expression)
烂程序示例:test.cpp
typedef void(PF)(int);
struct Point
{
int x;
int y;
};
int main(int argc, char *argv[])
{
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);
......
} //C++编译器可编译通过,但是不能运行
C方式强制类型转换存在的问题
—过于粗暴
—-任意类型之间都可以进行转换,编译器很难判断其正确性
// 现代软件工程中3大问题是bug的来源:
// 1.运算符优先级:当位运算,逻辑运算和数学运算混合在一起时,优先级对不对;
// 2.多线程编程时各个线程间的交互;
// 3.强制类型转换
—难于定位
—-在源码中无法快速定位所有使用强制类型转换的语句
在程序设计理论中强制类型转换是不被推荐的,与goto语句一样,应该尽量避免
强制类型转换在实际工程中还是可能使用的
如火如荼进行更加安全可靠的转换?
C++将强制类型转换分成4中不同的类型
用法:xxx_cast<Type>(Expression)
static_cast强制类型转换
——用于基本类型间的转换,但不能用于基本类型指针见的转换
——用于有继承关系类对象之间的转换和类指针之间的转换
int main(int argc, char *argv[])
{
int i = 0;
char c = 'c';
int* pi = &i;
char* pc = &c;
c = static_cast<char>(i); //It's OK!
pc = static_cast<char*>(pi); //Oops! invalid static_cast from type 'int*' to type 'char*'
......
}
static_cast是编译期进行转换的,无法在运行时检测类型,所以类型转换之间的转换可能存在风险。
const_cast强制类型转换
——用于去除变量的const属性
int main(int argc, char *argv[])
{
const int& j = 1;
int& k = const_cast<int&>(j); //将只读变量j降级为普通变量,k是j的引用,改变k就是改变j
const int x = 2; //常量x
int& y = const_cast<int&>(x); //常量x在强制类型转换为应用后,C++编译器给他分配了空间,引用y就是所分配空间的别名
k = 5;
printf("j = %d\n",j); //输出5
printf("k = %d\n",k); //输出5
y = 3;
printf("x = %d\n",x); //输出2 ,直接从符号表中拿
printf("y = %d\n",y); //输出3
printf("&x = %p\n",&x); //输出0022FF0C
printf("&y = %p\n",&y); //输出0022FF0C x和y地址完全一样
printf("Press enter to continue ...");
getchar();
return 0;
}
reinterpret_cast强制类型转换
——用于指针类型间的强制换行
——用于整数和指针类型间的强制转换
typedef void(PF)(int);
int main(int argc, char *argv[])
{
int i = 0;
char c = 'c';
int* pi = reinterpret_cast<int*>(&c);
char* pc = reinterpret_cast<char*>(&i);
PF* pf = reinterpret_cast<PF*>(0x12345678);
c = reinterpret_cast<char>(i); //Oops static_cast should be used here
......
}
reinterpret_cast直接从二进制位进行复制,是一种极其不安全的转换
dynamic_cast强制类型转换
——主要用于类层次间的转换,还可以用于类之间的交叉转换
——dynamic_cast具有类型检查功能,比static_cast更安全
小结
C++中内置了动态内存分配的专用关键字
C++中的动态内存分配是基于类型进行的
C++中命名空间概念用于解决名称冲突问题
C++细化了C语言中强制类型转换的方式
——C++不推荐的程序中使用强制类型转换
——C++建议在强制类型是考虑一下究竟希望什么样的转换