1、动态分配内存—-new
在C语言中,我们通常用malloc函数动态申请内存,用free函数释放动态申请的内存,这说明C语言本身是不支持动态申请内存的。在C++中,增加了新的关键字new来动态内存分配,增加了delete关键字来释放动态申请的内存;C++中的动态内存申请是基于类型进行的。
动态申请一个变量:Type *p = new Type;
释放动态申请的变量:delete p;
动态申请一个数组:Type *p = new Type[num];
释放动态申请的数组:delete[] p;
下面是一个例子:
int main(int argc,char *argv[])
{
int *p = new int;
*p = 5;
*p = *p + 10;
printf("p = %p\n",p);
printf("*p = %d\n",*p);
delete p;
p = new int[10];
for(int i=0;i<10;i++){
p[i] = i;
printf("p[%d] = %d\n",i,p[i]);
}
delete[] p;
return 0;
}
new关键字与malloc函数的区别:
1、new关键字是C++的一部分,malloc是由C库提供的函数;
2、new以具体类型为单位进行内存分配,malloc只能以字节为单位进行内存分配;
3、new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特性;
new关键字的初始化:Type *p = new Type(vale)
2、命名空间
在C语言中只有一个全局作用域,C语言中所有的全局标识符共享同一个作用域,这样标识符之间可能发生冲突。所以,C++中提出了命名空间的概念,命名空间将全局作用域分成不同的部分,不同命名空间中的标识符可以同名而不会发生冲突;命名空间可以相互嵌套,全局作用域也叫默认命名空间,命名空间用关键字namespace定义。格式为:namespace name { /* … */ }
;示例:
namespace First{
int i = 1;
}
namespace Second{
int i = 2;
namespace Internal{
struct p{
int x;
int y;
};
}
}
C++命名空间的使用:
当我们需要使用命名空间里的标识符时,我们可以使用空间名::标识符
的方式来使用命名空间里的标识符,例如我们使用First空间中的i,则可以这样使用:First::i
;所以使用默认命名空间中的标识符也可以这样:::Variable;
在main函数中使用上面空间中的标识符:
int main(int argc,char *argv[])
{
Second::Interval::P p = {4,8};
printf("First: i = %d\n",First::i);
printf("Second: i = %d\n",Second::i);
printf("p.x = %d\n",p.x);
printf("p.y = %d\n",p.y);
return 0;
}
编译通过,运行结果为:
First: i = 1
Second: i = 2
p.x = 4
p.y = 8
这样使用显得有些麻烦,有没有方法能让我们像平常一样只使用标识符就能使用它呢?就像平时我们使用全局变量一样。C++可以使用using namespace 空间名;
来打开命名空间,使我们在使用该语句所在的作用域内直接使用标识符名来使用该空间的标识符了。例如:
int main(int argc,char *argv[])
{
using namespace First;
printf("i = %d\n",i);
return 0;
}
编译成功,运行结果为:
i = 1
从结果我们可以看出,当我们使用using namespace 空间名;
后,我们便可以直接通标识符名使用标识符了。那么为什么我们在默认命名空间中定义的标识符也可以直接使用标识符呢?因为C++为了兼容C语言,在默认情况下是打开默认命名空间的。
我们也可以使用using 空间名::标识符
来简单的使用某个空间中的某个标识符,例如:using Second::Interval::P;
,这样我们就可以在该语句所在的作用域内直接使用 P ,而不用Second::Interval::P了。大家示例:
int main(int argc,char *argv[])
{
using Second::Interval::P;
P p = {4,8};
printf("p.x = %d\n",p.x);
printf("p.y = %d\n",p.y);
return 0;
}
编译成功,运行结果:
p.x = 4
p.y = 8
大家可能注意到,在上面一直强调在该语句所在的作用域内。那么下面我们用代码来看看是不是这样的,同样使用上面的命名空间。代码如下:
void function(void)
{
printf("i = %d\n",i);
}
int main(int argc,char *argv[])
{
using namespace First;
function();
return 0;
}
编译报错:
main.cpp: In function ‘void function()’:
main.cpp:26:23: error: ‘i’ was not declared in this scope
printf("i = %d\n",i);
^
main.cpp:26:23: note: suggested alternatives:
main.cpp:12:9: note: ‘First::i’
int i = 1;
^
main.cpp:16:9: note: ‘Second::i’
int i = 2;
在上面的例子中,我们在using namespace 空间名;
语句所在作用域之外直接使用标识符,编译器报错了,这就说明,我们只能在该语句所在的作用域内直接使用标识符。
那么我们在相同的作用域内打开两个具有相同标识符的命名空间会是怎么样的呢?
int main(int argc,char *argv[])
{
using namespace First;
using namespace Second;
return 0;
}
编译通过了,这说明是能同时打开两个具有相同标识符的命名空间的,那么我们直接使用哪个相同的标识符,编译器会选择哪个呢?这不就产生了二义性了吗?
int main(int argc,char *argv[])
{
using namespace First;
using namespace Second;
printf("i = %d\n",i);
return 0;
}
编译报错了,在这种情况下,出现了二义性,编译器不知道到底该使用哪一个标识符了。
在命名空间中不仅可以定义变量,还可以定义结构类型,函数等。
3、强制类型转换
C语言的方式:( Type )( Expression ) or Type( Expression )
C语言的强制类型转换存在的问题:
1、过于粗暴。热议类型之间都可以进行转换,编译器很难判断其正确性。
2、难于定位。在源码中无法快速定位所有使用强制类型转换的语句。
注意:在程序设计理论中强制类型转换是不被推荐的,与goto语句一样,应该尽量避免。
C++中:C++将强制类型转换分为4种不同的类型,有:static_cast、const_cast、reinterpret_cast和dynamic_cast。用法:xxx_cast< Type >( Expression )
static_cast:用于基本类型间的转换,但不能用于基本类型指针间的转换;用于有继承关系类对象之间的转换和类指针之间的转换。
static_cast是编译期进行转换的,无法在运行时检测类型,所以类类型之间的转换可能存在风险。
const_cast:用于去除变量的const属性。
reinterpret_cast:用于指针类型间的强制转换;用于整数和指针类型间的强制转换。
reinterpret_cast:直接从二进制位进行复制,是一种极其不安全的转换。
dynamic_cast:主要用于类层次间的转换,还可以用于类之间的交叉转换;dynamic_cast具有类型检查的功能,比static_cast更安全。