zoukankan      html  css  js  c++  java
  • new

    https://en.cppreference.com/w/cpp/language/new
    https://zh.cppreference.com/w/cpp/language/new

    new表达式

    创建并且初始化动态存储的对象,对象的声明周期并不受创建作用域的限制。这里很明确的说明了new是创建并且初始化,并且是对象。那么new的考点就是:

    • new与malloc的不同

      • malloc是申请一块内存。malloc更简洁,仅仅是分配对应大小的内存,分配好之后给我们使用,具体如何使用由我们自己决定,比如初始化,或者当作数组
      • new是创建对象。有了更详细的区分,对不不同类型的对象,比如常见变量类型int,数组或者类都有着细微的区别,更方便我们的使用
    • new的初始化。对不同对象的初始化写法以及结果

    • new[]。申请数组的方法和初始化方法

    上面只是大概的考点,还有其他的一些细节,在下面会一一介绍。

    语法

    ::(optional) new (placement_params)(optional) ( type ) initializer(optional)
    
    ::(optional) new (placement_params)(optional) type initializer(optional)
    

    new有两种语法,两个的唯一区别就是type是否有括号,之所以有这个区别是因为

    • 动态数组只能用第二种无括号的创建
    • 由于符号优先级以及聚合的原因,有些类型只能用第一种方式,用括号把类型组合在一起

    括号的用法

    //这个是错误的,由于优先级组合的原因,导致编译的时候把new int作为一个整体,那么就是申请一个int类型的空间,而后面的(*[10])()是不符合语法的,无法解析,所以会报错
    new int(*[10])(); // error: parsed as (new int) (*[10]) ()
    
    //这样就很明确了,括号内int(*[10])()是一个整体,看作一个类型,这个类型是保存函数指针的10个数组元素
    new (int (*[10])()); // okay: allocates an array of 10 pointers to functions
    

    上面的int (*[10])()如何解析,请看数组、函数与指针

    无括号的优先级组合规则

    如果无括号,new的规则是尽可能的与后面有意义的符号组合在一起,如下

    //正确,申请一个int,并且把这个指针加一,虽然这个没有意义,本身是有问题的,但是从语法上说是正确的
    new int + 1; // okay: parsed as (new int) + 1, increments a pointer returned by new int
    
    //错误,由于new的贪婪的组合规则,导致会把new int *看作一个整体,那么后面的1就无法解析
    //就算是写成(new int)*1也是错误的,因为指针没有乘除法,只有加减法
    //如果按照默认组合规则如何才能正确呢,需要改成new int*((int*)(1)),这就相当于申请了一个int*的类型,初始化为1。因为1是整数,所以默认情况会提示类型不匹配,强制转换就匹配了,虽然这是无意义的
    new int * 1; // error: parsed as (new int*) (1)
    

    必须存在的初始化器

    对于最后的一个参数initializer是可选的,但是有些情况是必须的:

    • 未知边界的数组
    • 使用了占位符
      • auto (C++11 起)
      • decltype(auto) (C++14 起)
      • auto or decltype(auto),附带类型约束 (C++20 起)
    • 使用了类模板,且该模板有实参需要被推导

    上面三种情况必须要明确初始化,因为后面两种如果不初始化,就无法得知创建的类型,第一种如果不初始化,就无法得知空间大小

    //创建一个double的数组,数组的个数没有给,由后面的初始化表达式{1,2,3}确定
    double* p = new double[]{1,2,3}; // creates an array of type double[3]
    
    //创建一个auto类型的对象,类型不知,由auto('c')推导出是char,初始化为c
    auto p = new auto('c');          // creates a single object of type char. p is a char*
     
    //可以通过auto(1)推导出是int
    auto q = new std::integral auto(1);         // OK: q is a int*
    
    //前面是floating_pint,而后面是auto(true)是一个布尔类型,所以不匹配,是错误的
    auto q = new std::floating_point auto(true) // ERROR: type constraint not satisfied
    
    //可以有(1,true)得到pair的类型是<int, bool>
    auto r = new std::pair(1, true); // OK: r is a std::pair<int, bool>*
    
    //这个是错误的,因为无法知道vector的类型,所以就不知道创建多少空间
    //可以改写成auto r = new std::vecotr<int>; 这样就是创建一个int类型的vector
    //也可以写成auto r = new vector<int>(3); 这样就是创建一个int类型的vector,有3个元素
    //auto r1 = new vector<int>[3]; 创建一个vector<int>的数组,数组有3个元素
    //auto r2 = new vector<int>{ 1,2,3 }; 创建一个int类型的vector,初始化为1 2 3,有三个元素
    auto r = new std::vector;        // ERROR: element type can't be deduced
    

    数组

    https://zh.cppreference.com/w/cpp/language/array#.E6.9C.AA.E7.9F.A5.E8.BE.B9.E7.95.8C.E6.95.B0.E7.BB.84

    除了第一维是未知的以外,其余维度必须是正的常量整数。因为数组元素不能是未知边界数组,所以未知边界数组只有第一维度是可扩展的。因为数组会把除了第一维度的其他维度看作第一维度数组的元素,所以只能第一维度是未知。

    int n = 42;
    
    //直接创建是不允许的,所有维度必须都是常量
    double a[n][5]; // error
    
    //可以
    auto p1 = new  double[n][5];  // OK
    
    //不可以,第二维度不能是未知的
    auto p2 = new  double[5][n];  // error: only the first dimension may be non-constant
    
    //在visual studio 2017最新版本上测试是可以,可能编译器对括号这种方式做了优化
    auto p3 = new (double[n][5]); // error: syntax (1) cannot be used for dynamic arrays
    

    注意事项

    • 如果第一维变量是负数,C++11之前行为未知,C++11之后会抛出异常std::bad_array_new_length,如果没有捕获异常会导致程序崩溃,如果不想捕获异常,而是通过空指针判断需要增加nothrow,用法如下
    auto p1 = new (nothrow) double[n][5];
    
    • 如果所有维度都是常量可以确定数组大小,再使用{}进行初始化的时候,如果初始化的元素个数大于申请的数组元素,会编译报错
    auto p = new int[2]{1,2,3};//报错
    
    • 如果第一维度是未知的,程序不会有任何问题,但是就会导致数组越界,产生更严重并且很难查询的问题
    int n = 2;
    auto p = new int [n]{1,2,3};
    
    • 如果维度是0,这个是允许的,也是正常的,调用delete[]也不会有问题,但是这个仅仅相当于定义了一个指针,不能对其赋值,因为是0空间,所以没有申请内存,对其赋值操作会导致覆盖了其他正常的元素

    构造规则

    T不是数组

    • new T 创建一个T的对象,用默认初始化

    • new T(n) 创建一个T的对象,初始化为n,这里如果n是模板或者类,调用的是其对应的构造函数,比如

    auto p = new vector<int>(4);
    

    这里并不是创建了一个vector里面有一个元素,赋值为4,这里是创建了一个vector,size大小是4

    • new T{n1,n2,...} 创建一个T对象,初始化为n1,n2,...,这个用法可以用作类,结构体(有多个成员),容器(vector)和常见的基本类型
    struct test
    {
        int a;
        char b;
    };
    void main(void)
    {
        auto p1 = new test{ 1,'c' };
        auto p2 = new vector<int>{ 1,2,3 };
        auto p3 = new int{ 1 };
    }
    

    T是数组

    • new T[n] 创建T的数组,默认初始化

    • new Tn 创建T的数组,并且对每一个元素初始化。这里注意是空括号,括号内并没有参数,初始化的规则基本上是数字是0,字符是0,字符串是空,下面这些是合法的

    struct test
    {
        int a;
        char b;
    };
    void main(void)
    {
        auto p1 = new test[2]();
        auto p2 = new vector<int>[2]();
        auto p3 = new int[3]();
        auto p4 = new char[5]();
    }
    
    • new T[n]{a1, a2, a3...} 创建T的数组,并且按照顺序聚合初始化元素
    struct test
    {
        int a;
        char b;
    };
    void main(void)
    {
        //这个测试下来是无效的,并没有按照我们写的初始化,但是没有报错
        auto p1 = new test[2]{ {1, 'a'}, {2, 'c'} };
    
        auto p2 = new vector<int>[2]{ {1},{2} };
        auto p3 = new int[3]{ 1,2,3 };
        auto p4 = new char[5]{'a', 'b', 'c'};
    }
    

    版权声明:本文版权归作者所有,如需转载,请标明出处

  • 相关阅读:
    SFML从入门到放弃(3) 视角和碰撞检测
    SFML从入门到放弃(2) 图像和音频
    SFML从入门到放弃(1) 窗口和交互
    SFML从入门到放弃(0) 配置环境
    NOI2017 酱油记
    【bzoj4889】: [Tjoi2017]不勤劳的图书管理员 分块-BIT
    【bzoj4888】: [Tjoi2017]异或和 BIT-乱搞
    【bzoj4887】:[Tjoi2017]可乐 矩阵乘法,快速幂
    THUSC2017酱油记
    CTSC2017酱油记
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/12093013.html
Copyright © 2011-2022 走看看