【七】C++一些新的关键字 -- new、delete、强制类型转换

目录

1、动态内存分配

C++中的动态内存分配已经和C语言有了明显区别:

  • C++中通过new关键字进行动态内存申请
  • C++中的动态内存申请是基于类型进行的
  • delete关键字用于内存释放

 
变量申请和释放:

Type* pointer = new Type;
// ……
delete pointer;

 
数组申请和释放:

Type* pointer = new Type[N];
// ……
delete[] pointer;

 
 
new关键字与malloc函数的区别:

  • new关键字是C++的一部分,malloc是由C库提供的函数
  • new以具体类型为单位进行内存分配,malloc只能以字节为单位进行内存分配
  • new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特性
  • new对基本类型申请空间后默认初始化为0
  • 对于类类型new和delete还负责构造函数析构函数的调用

 
示例:
exp-1.cpp

#include <iostream>

using namespace std;

int main()
{
    //申请数组,并默认初始化为0
    int **p = new int*[10];
    for(int i = 0; i < 10; i++)
    {
        cout << p[i] << '\t';
    }
    cout << endl;

    //申请单个变量,并指定初始值
    int *a = new int(5);
    cout << "*a = " << *a << endl;

    //申请单个变量,并默认初始化为0
    double *d = new double;
    cout << "*d = " << *d << endl;

    delete [] p;
    delete a;
    delete d;

    return 0;
}

运行结果:

这里写图片描述

 
 

2、命名空间

在C语言中只有一个全局作用域

  • C语言中所有的全局标识符共享同一个作用域
  • 标识符之间可能发生冲突

C++中提出了命名空间的概念

  • 命名空间将全局作用域分成不同的部分
  • 不同命名空间中的标识符可以同名而不会发生冲突
  • 命名空间可以相互嵌套
  • 全局作用域也叫默认命名空间

 
C++命名空间的定义:

namespace name { /* … */ }

 
C++命名空间的使用:

  • 使用整个命名空间:using namespace name;
  • 使用命名空间中的变量:using name::variable;
  • 使用默认命名空间中的变量:::variable;
  • 默认情况下可以直接使用默认命名空间中的所有标识符:variable;

 
示例:
exp-2.cpp

#include <iostream>

using namespace std;

namespace First
{
    int i = 0;
}

namespace Second
{
    int i = 10;
    namespace Internal
    {
        struct Student
        {
            const char *name;
            int age;
        };
    }
}

int main()
{
    using Second::Internal::Student;

    First::i = 5;

    Student stu;
    stu.name = "Tom";
    stu.age = First::i + Second::i;

    cout << "Name:" << stu.name << "\n" << "Age:" << stu.age << endl;
    return 0;
}

 
 

3、强制类型转换

 

C方式的强制类型转换:

  • ( Type )( Expression )
  • Type( Expression )

示例:
exp-3.cpp

#include <stdio.h>

typedef void(PF) (int);

struct Point
{
    int x;
    int y;
};

int main()
{
    //定义一个整数
    int v = 0x12345;

    //下面就开始乱来了!想怎么搞就怎么搞!
    PF *pf = (PF*)v;
    char c = char(v);

    //我都不知道我在写什么....
    pf(c);

    Point *p = (Point*)v;

    printf("Point::x = %d\n", p->x);
    printf("Point::y = %d\n", p->y);
    return 0;
}

上面的程序,编译能过,最多也就给个警告,但是运行肯定出错!

 
C方式强制类型转换存在的问题

  • 过于粗暴
    • 任意类型之间都可以进行转换,编译器很难判断其正确性
  • 难于定位
    • 在源码中无法快速定位所有使用强制类型转换的语句

如何进行更加安全可靠的转换呢?

 

C++强制类型转换

  • C++将强制类型转换分为4种不同的类型

这里写图片描述

 

static_cast强制类型转换
  • 用于任何具有明确定义的类型之间的转换,只要不包含底层const,都可以使用!
    • const char *cp = “C++”; char *q = static_cast< char* >(cp);
    • 上面这句就是错误的,static_cast不能去掉底层const
  • 用于基本类型间的转换,但不能用于基本类型指针间的转换!
  • 用于有继承关系类对象之间的转换和类指针之间的转换!

 
Tip:

  static_cast是编译期进行转换的,无法在运行时检测类型,所以类类型之间的转换可能存在风险。

示例:
exp-4.cpp

#include <iostream>
using namespace std;

int main()
{
    int i = 97;
    char c;

    c = static_cast<char>(i);       //OK!
    i = static_cast<int>(c);        //OK!

    char *pa = static_cast<char*>(&i);  Error!!

    cout << "char c = '" << c << "'"<<endl;
    cout << "int i = " << i <<endl;

    const char *cp = "C++";
    char *q = static_cast< char* >(cp); //Error!!

    return 0;
}

 

const_cast强制类型转换
  • 用于去除变量的const属性
  • 只能改变运算对象的底层const

示例:
exp-5.cpp

#include <stdio.h>

int main()
{
    //这样声明,相当于得到了一个只读变量,而非常量
    const int& j = 1; //相当于: int temp = 1; const int&j = temp;  //temp来自符号表

    int& k = const_cast<int&>(j); //对j这个只读变量取引用,并强制去掉了它的const属性:const int* const ==> int* const,得到了一个别名为k的普通变量

    k = 5;

    printf("k = %d\n", k);  //普通变量,来自存储空间
    printf("j = %d\n", j);  //只读变量,来自存储空间
    printf("&k = %p\n", &k);
    printf("&j = %p\n", &j);

    //这里才是声明了一个常量,符号表中会有它
    const int x = 2;
    int& y = const_cast<int&>(x); 
    //这里对x这个常量取了引用,引用的本质是指针常量,所以导致编译器为常量x分配了存储空间
    //然后,这里的const_cast相当于转换了:const int* const类型的指针为int *const类型
    //而且,这里不能这些做:int& y = x; 会报错!!因为如果没有const_cast<int&>(x);那么x始终都还是一个常量,没有存储空间,不是只读变量!

    y = 8;

    printf("x = %d\n", x);  //这里用的是常量,来自符号表
    printf("y = %d\n", y);  //这里用的是只读变量,来自存储空间
    printf("&x = %p\n", &x);
    printf("&y = %p\n", &y);

    return 0;
}

运行结果:

这里写图片描述

 
Tip:
  关于以上程序运行结果的本质原因,在前面的文章中说过,请看这里

 

当volatile关键字作用于“常量”
  • volatile关键字,告诉编译器不做任何的优化,每次直接到内存中存取值!
  • volatile关键字的详细介绍,请看这里

思考?
  经过volatile关键字后,const int i = 1; 还是一个常量么?
验证方法:
  我们取i的地址并去掉它的底层const属性,假设它还是一个常量,那么通过指针修改i的内存值后,i的输出值不会改变,因为常量的值来自于符号表;如果值改变了,说明i已经成为了一个只读变量,它的值来自于存储空间!

示例:
exp-6.cpp

#include <stdio.h>

int main()
{
    volatile const int i = 1; 

    int *p = const_cast<int*>(i);

    *p = 100;

    printf("i = %d\n", i);
    printf("*p = %d\n", *p);

    return 0;
}

运行结果:

这里写图片描述

 
Tip:
  从结果上表明,volatile 关键字作用于常量时,常量会变成只读变量,不再使用符号表,而直接使用编译器为“常量”分配的内存空间中的值!

 

reinterpret_cast强制类型转换
  • 用于指针类型间的强制转换
  • 用于整数指针类型间的强制转换
  • 通常为运算对象的位模式提供较低层次上的重新解释,直接复制二进制位来实现类型转换,是一种极其不安全的转换!

 
示例:
exp-6.cpp

#include <stdio.h>

int main()
{
    int i = 0;
    char c = 'c';
    int* pi = &i;
    char* pc = &c;

    pc = reinterpret_cast<char*>(pi);
    pi = reinterpret_cast<int*>(pc);

    pi = reinterpret_cast<int*>(i);

    c = reinterpret_cast<char>(i); // Error!

    return 0;
}

 

dynamic_cast强制类型转换
  • 主要用于类层次间的转换,还可以用于类之间的交叉转换
  • dynamic_cast具有类型检查的功能,比static_cast更安全
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值