动态内存分配
- C++中的动态内存分配
—C++中通过new关键字进行动态内存申请(malloc属于标准C,不属于C++)
—C++中的动态内存申请时基于类型进行的
—delete关键字用于内存释放
变量申请:
Type* pointer = new Type;
// ……
delete pointer;
数组申请:
Type* pointer = new Type[N];
// ……
delete[] pointer;
int main()
{
int * p = new int;
*p = 5;
*p = *p + 10;
printf("p = %p\n",p);
printf("*p = %d\n,*p");
delete p; // 将p指向的堆空间中的1个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; // 将p指向的堆空间中的整个数组大小(10个int大小内存)释放
//如果只是delete p;则会造成内存泄漏(9个int大小内存泄漏)
printf("Press any key to continue...");
getchar();
teturn 0;
}
new关键字与malloc函数的却别
*new关键字时C++的一部分,malloc时由C库提供的函数(在C++中照样能调用malloc,因为malloc只是一个函数)
*new以具体类型为单位进行内存分配,malloc只能以字节为单位进行内存分配
*new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特性new关键字的初始化
int main()
{
int* pi = new int[1];
int* pf = new float[2.0f];//这里2.0f中的f表示2.0是一个浮点数
char* pc = new char['c'];
printf("*pi = %d\n",*pi);
printf("*pf = %d\n",*pf);
printf("*pc = %d\n",*pc);
delete pi;
delete pf;
delete pc;
printf("Press ang key to continue ...");
getchar();
return 0;
}
C++中的命名空间
- 在C语言中只有一个全局作用域
*C语言中所有的全局标识符共享同一个作用域
(标识符之间可能发生冲突) C++中提出了命名空间的概念
*命名空间将全局作用域分成不同的部分
*不同命名空间中的标识符可以同名而不会发生冲突
*命名空间可以互相嵌套
*全局作用域也叫默认命名空间C++命名空间的定义:namespace name{…}
//以下编译能通过,因为,虽然定义了2个int i 但是命名空间将全局作用域分成不同的部分
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
printf("Press any key to continue ...");
getchar();
return 0;
}
- C++命名空间的使用:
*使用整个命名空间:using namespace name;
*使用命名空间中的变量:using name::variable;
*使用默认命名空间中的变量: ::variable
默认情况下可以直接使用默认命名空间中的所有标识符
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
using namespace First;
using Second::Internal::P;
printf("i = %d\n",i);
printf("i = %d\n",Second::i);
P p = {2,3};
printf("p.x = %d\n",p.x);
printf("p.y = %d\n",p.y);
printf("Press any key to continue ...");
getchar();
return 0;
}
强制类型转换
- C方式的强制类型转换
(Type)(Expression) or Type(Expression)
//以下程序虽然编译通过,但是运行是不成功的。
//C方式的强制类型转换很容易通过,但不一定运行得到你想要的结果(因为编译通过并不代表合理)
typedef void(PF)(int);//定义一个函数类型,参数是1个int类型的值,返回值无的函数类型,这个函数类型名为PF
struct Point
{
int x;
int y;
};
int main()
{
int v = 0x12345;
PF* pf = (PF*)v;
char c = char(v);
pf(v);
point* po = (Point*)v;
printf("(%d,%d)\n",po->x,po->y);
return 0;
}
- C方式强制类型转换存在的问题
->过于粗暴
__->任意类型之间都可以进行转换,编译器很难判断其正确性
->难于定位
__->在源码中无法快速定位所有使用强制类型转换的语句
在程序设计理论中强制类型转换是不被推荐的,与goto语句一样,应该尽量避免。
- 强制类型转换在实际工程中还是可能使用的
如何进行更加安全可靠的转换? C++将强制类型转换分为4种不同的种类
- 强制类型转换
static_cast | const_cast |
dynamic_cast | reinterpret |
用法:XXX_cast< Type >(Expression)
- static_cast强制类型转换
__用于基本类型间的转换,但不能用于基本类型指针间的转换
__用于有继承关系类对象之间的转换和类指针之间的转换
int main()
{
int i = 0;
char c = 'c';
int* pi = &i;
char* pc = &c;
c = static_cast<char>(i);//OK!
pc = static_cast<char*>(pi);//Oops!//不能用于基本类型指针间的转换
return 0;
}
static_cast是编译期进行转换的,无法在运行时检测类型,所以类类型之间的转换可能存在风险
- **const_cast强制类型转换
__用于去除变量的const属性
int main()
{
const int& j = 1;//const引用,功能是定义的变量不能作为左值使用,但是依然可以用指针解引用的方式去改变其存放的值。这里用了另外的方式去改变其值
int& k = const_cast<int&>(j);//先去除变量j的const的属性,然后为变量j另外取一个名字k(注意:这只是在这一句实现了k的一种功能,不能改变j的功能,j的功能在上一句已经定好了!)
const int x = 2;
int& y = const_cast<int&>(x);//先去除变量x的const属性让其变成普通变量,然后为这个普通变量x取一个别名y(注意:这只是在这一句实现了y的一种功能,不能改变x的功能,x的功能在上一句已经定好了!)同时也为x(y是其别名)了一块存储空间,但是这个存储空间很神奇,因为每次使用到x的时候都是从常量表中去取2这个值,所以这个空间基本就是给y用的,但是x和y又是一个东西,哈哈哈。
k = 5;
printf("j = %d\n",j);//打印出5,因为k是j的别名,k的值改变了,j也就改变了
printf("k = %d\n",k);//打印出5,这个很明显,k = 5
y = 3;
printf("x = %d\n",x);//由于const int定义的,所以x是存放在常量表里面的一个常量值2
printf("y = %d\n",x);//很明显y = 3
printf("&x = %p\n",&x);//这里x和y的地址是一样的。虽然x和y的值不同,那是因为x和y所代表的存储空间虽然是同一个,但是每次使用x变量不是从存储空间去取值而是从常量表里去取值,所以x的值都是2;但是y却不是一个常量,所以一旦改变了存储空间中的值,y的值就改变了;
printf("&y = %p\n",&y);表的那个存储空间是相同的
return 0;
}
- reinterpret_cast强制类型转换
__用于指针类型间的强制转换
__用于整数和指针类型间的强制转换
typedef void(PF)(int);
int main()
{
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实现基本类型间的转换
}
reinterpret_cast直接从二进制位进行赋值,是一种极其不安全的转换。
- dynamic_cast强制类型转换
*主要用于类层次间的转换,还可以用于类之间的交叉转换
*dynamic__cast具有类型检查的功能,比static_cast更安全
类是什么?对象是什么?类层次又是什么?
小结
- C++中内置了动态内存分配的专用关键字
- C++中的动态内存分配是基于类型进行的
- C++中命名空间概念用于解决名称冲突问题
- C++细化了C语言中强制类型转换的方式
——C++不推荐在程序中使用强制类型转换
——C++建议在强制类型转换时考虑一下究竟希望什么样的转换
下面将开始学习面向对象程序的设计了!!!