回想C语言中,动态内存是怎么分配的?通过C库里面的malloc free去进行动态内存分配。
C++通过new关键字进行动态内存申请,动态内存申请是基于类型进行的。
delete 关键字用于内存释放。
//变量申请
type* pointer = new type;
delete pointer;
//数组申请
type* pointer = new tyep[10];
delete[] pointer;
//数组释放一定要加 []
int *p = new int[10];
//.......
delete p;//会造成内存泄漏。这句话的意思是告诉编译器只释放p指向的一个int型空间,而不是所有的数组空间。
delete [] p;//正确的写法
#include
int main()
{
int* a = new int(6);
float* b = new float(7.0);
char* c = new char('c');
printf("*a = %d, *b = %f, *c = %c\n", *a, *b, *c);
delete a;
delete b;
delete c;
return 0;
}
在C语言中,只有一个全局作用域。C语言中所有的全局标识符共享同一个作用域,那么项目很大时,标识符之间就有可能发生冲突。
C++中提出了命名空间的概念。目的是将全局作用域划分为不同的块。
命名空间定义:
namespace name { /…/}
#include
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
return 0;
}
使用整个命名空间: using namespace name;
使用命名空间中的变量:using name::variable
使用默认命名空间中的变量:::variable
默认情况下,可以直接使用默认命名空间中所有的标识符。
#include
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);//打印0,使用的是First 空间中的i。因为我们没有解开Second 空间
printf("i = %d\n", Second::i);//打印1,告诉编译器访问的是Second 空间中的i。
P p = {2, 3};
printf("p.x=%d, p.y=%d\n", p.x, p.y);
return 0;
}
C方式的强制类型转换(type)(expression) or type(expression)
#include
//定义一个函数指针
typedef void(PF)(int);
struct Point
{
int x;
int y;
};
int main()
{
int v = 0x12345;
//将变量v强制类型转换为函数指针
PF* pf = (PF*)v;
//将变量v强制类型转换为char型
char c = char(v);
//调用v变量强制转换为函数的地址函数,此处不一定是函数。
pf(v);
//将v变量强制转换为结构体指针。
Point* p = (Point*) v;
printf("p->x = %d\n", p->x);
printf("p->y = %d\n", p->y);
}
C强制类型转换过于粗暴,任意类型之间都可以进行转换,编译器很难判断其正确性;且在源码中无法快速定位所有使用强制类型转换的语句,出现问题时难以定位。
在现代软件工程中,最难定位问题的三种问题:
1.强制类型转换。
2.多线程的交互。
3.位运算优先级,数学运算、逻辑运算混在一起。
在程序设计理论中,强制类型转换也是不被推荐的,与goto语句一样,尽量避免。
C++提供了更安全的强制类型转换。将强制类型转换分为4中不同的类型。static_cast const_cast dynamic_cast reinterpret_cast
。用法xxx_cast
#include
int main()
{
int i = 8;
char c = 'B';
int* pi = &i;
char * pc = &c;
c = static_cast<char>(i);
pc = static_cast<char*>(pi);
return 0;
}
static_cast是编译期进行转换的,无法在运行时检测类型。所以类类型之间的转换可能存在风险。
#include
int main()
{
//const 引用j,初始化的时候j就是一个只读变量。
//注意第二节讲到 const引用只有定义的别名拥有只读属性,不会影响到正名
const int& j = 1;
//将只读变量j的const属性去掉,j就降级为普通变量。
int& k = const_cast<int&>(j);
//声明一个真正的常量x。
const int x = 2;
//由于对x常量取引用,前面学习了,引用本身就是地址别名,是指针。
//所以此处编译器会为这个常量分配一个空间。
//y就指向了常量x的地址,y就变成了普通变量。
int& y = const_cast<int&>(x);
//将j的值改为6
k = 6;
//j 和 k的值一致。
printf("j = %d,k = %d\n", j, k);
//将x的变量改成8
y = 8;
//所以x 和 y的地址一样的。
//const引用只有定义的别名拥有只读属性,不会影响到正名,所以x的值还是2,因为是常量,编译器直接替换。
printf("x = %d, y = %d\n", x, y);
printf("&x = %p, &y = %p\n", &x, &y);
return 0;
}
上面程序中运用到了第二节的知识点:const 常量只有定义的别名拥有只读属性,不会影响到正名。
int& y = const_cast
在解除const属性时,会为x分配内存空间,此时引用y指向这段编译器为x分配的空间,引用y就是普通变量。但是x还是常量,编译器依然认为其不能被赋值。假如下面有一段语句x=8;
则编译器还是会报错。
#include
typedef void (PF)(int);
int main()
{
int i = 0;
char c = 'C';
//将字符变量c的地址强制转换为int型指针,并且赋值给pi指针。
int* pi = reinterpret_cast<int*>(&c);
//将int变量i的地址强制转换为char型指针,并赋值给pc指针。
char* pc = reinterpret_cast<char*>(&i);
//将0x888888地址,强制转换为PF型函数指针,并赋值给pf指针。
PF* pf = reinterpret_cast<PF*>(0x888888);
//将int类型变量转换为char,不是用reinterpret_cast,而是static_cast
c = reinterpret_cast<char>(i);
return 0;
}
类是什么?对象是什么?类层次是什么?