C++初步(4)

  1. 动态内存分配 (Dynamic memory)
    需要内存大小为一个变量,其数值只有在程序运行时 (runtime)才能确定。
    操作符 new 和 delete 是C++执行指令
    操作符new的存在是为了要求动态内存。new 后面跟一个数据类型,并跟一对可选的方括号[ ]里面为要求的元素数。它返回一个指向内存块开始位置的指针。其形式为:

    pointer = new type
    

    或者

    pointer = new type [elements]
    

    第一个表达式用来给一个单元素的数据类型分配内存。第二个表达式用来给一个数组分配内存。
    例如:

    int * bobby;
    bobby = new int [5];
    

    现在bobby 指向一块可存储5个整型元素的合法的内存空间。
    给指针分配内存空间与定义一个普通的数组有什么不同。最重要的不同是,数组的长度必须是一个常量,这就将它的大小在程序执行之前的设计阶段就被决定了。而采用动态内存分配,数组的长度可以常量或变量,其值可以在程序执行过程中再确定。
    动态内存分配通常由操作系统控制,在多任务的环境中,它可以被多个应用(applications)共享因此内存有可能被用光。如果这种情况发生,操作系统将不能在遇到操作符new 时分配所需的内存,一个无效指针(null pointer)将被返回。因此,建议在使用new之后总是检查返回的指针是否为空(null),如下例所示:

    int * bobby;
    bobby = new int [5];
    if (bobby == NULL) {
    // error assigning memory. Take measures.
    };
    

    动态分配的内存只是在程序运行的某一具体阶段才有用,一旦它不再被需要时就应该被释放,以便给后面的内存申请使用。操作符delete 它的形式是:

    delete pointer;//删除给单个元素分配的内存
    

    delete [ ] pointer;//删除多元素(数组)的内存分配
    

    (在多数编译器中两种表达式等价,使用没有区别, 虽然它们实际上是两种不同的操作,需要考虑操作符重载overloading 。)

    // rememb-o-matic
    #include ‹iostream.h›
    #include ‹stdlib.h›
    
    int main ( ) {
        char input [100];
        int i,n;
        long * l;
        cout << "How many numbers do you want to type in? ";
        cin.getline (input,100); 
        i = atoi (input);
        l = new long[i];
        if (l == NULL) 
            exit (1);
        for (n=0; n<i; n++) {
            cout << "Enter number: ";
            cin.getline (input,100); 
            l[n]=atol (input);
        }
        cout << "You have entered: ";
        for (n=0; n<i; n++) {
            cout << l[n] << ", ";
            delete[] l;
        }
        return 0;
    }
    //输出:
    How many numbers do you want to type in? 5
    Enter number : 75
    Enter number : 436
    Enter number : 1067
    Enter number : 8
    Enter number : 32
    You have entered: 75, 436, 1067, 8, 32,
    

    NULL是C++库中定义的一个常量,专门设计用来指代空指针的。如果这个常量没有被预先定义,你可以自己定以它为0:

    #define NULL 0
    

    在检查指针的时候,0和NULL并没有区别。但用NULL 来表示空指针更为常用,并且更易懂。原因是指针很少被用来比较大小或被直接赋予一个除0以外的数字常量,使用NULL,这一赋值行为就被符号化了。


    另:
    C中:
    malloc();
    给指针动态分配内存的通用函数。它的原型是:

    void * malloc (size_t nbytes);
    

    其中nbytes 是我们想要给指针分配的内存字节数。这个函数返回一个void*类型的指针,因此我们需要用类型转换(type cast)来把它转换成目标指针所需要的数据类型,例如:

    char * ronny;
    ronny = (char *) malloc (10);
    

    这个例子将一个指向10个字节可用空间的指针赋给ronny。当想给一组除char 以外的类型(不是1字节长度的)的数值分配内存的时候,我们需要用元素数乘以每个元素的长度来确定所需内存的大小。
    操作符sizeof,它可以返回一个具体数据类型的长度。

    int * bobby;
    bobby = (int *) malloc (5 * sizeof(int));
    

    这一小段代码将一个指向可存储5个int型整数的内存块的指针赋给bobby,它的实际长度可能是 2,4或更多字节数,取决于程序是在什么操作系统下被编译的。
    还有calloc();

    void * calloc (size_t nelements, size_t size);
    

    它接收2个参数而不是1个。这两个参数相乘被用来计算所需内存块的总长度。通常第一个参数(nelements)是元素的个数,第二个参数 (size) 被用来表示每个元素的长度。可以像下面这样用calloc定义bobby:

    int * bobby;
    bobby = (int *) calloc (5, sizeof(int));
    

    malloc 和calloc的另一点不同在于calloc 会将所有的元素初始化为0
    以及realloc();
    它被用来改变已经被分配给一个指针的内存的长度。

    void * realloc (void * pointer, size_t size);
    

    参数pointer 用来传递一个已经被分配内存的指针或一个空指针,而参数size 用来指明新的内存长度。这个函数给指针分配size 字节的内存。这个函数可能需要改变内存块的地址以便能够分配足够的内存来满足新的长度要求。在这种情况下,指针当前所指的内存中的数据内容将会被拷贝到新的地址中,以保证现存数据不会丢失。函数返回新的指针地址。如果新的内存尺寸不能够被满足,函数将会返回一个空指针,但原来参数中的指针pointer 及其内容保持不变。
    最后 free();
    这个函数用来释放被前面malloc, calloc 或realloc所分配的内存块。

    void free (void * pointer);
    

    这个函数只能被用来释放由函数malloc, calloc 和realloc所分配的空间。

  2. 数据结构 (Data Structures)
    一个数据结构是组合到同一定义下的一组不同类型的数据,各个数据类型的长度可能不同。

     struct model_name {
        type1 element1;
        type2 element2;
        type3 element3;
        .
        .
    } object_name;
    

    这里model_name 是一个这个结构类型的模块名称。object_name 为可选参数,是一个或多个具体结构对象的标识。在花括号{ }内是组成这一结构的各个元素的类型和子标识

    struct products {
    char name [30];
    float price;
    }apple, orange, melon;
    ----------------------
    struct products {
    char name [30];
    float price;
    };
    products apple; 
    products orange, melon;
    -----------------------
    apple.name//每一个都有对应的数据类型
    ----------
    ...
    struct movies_t {
        char title [50];
        int year;
    }mine, yours;
    
    void printmovie (movies_t movie);//这里新定义结构体类型的使用。。。
    ...
    void printmovie (movies_t movie) {
        cout << movie.title;
        cout << " (" << movie.year << ")\n";
    }
    -----------------------------------------
    

    结构经常被用来建立数据库,特别是当我们考虑结构数组的时候。

    ...
    struct movies_t {
        char title [50];
        int year;
    } films [N_MOVIES];
    void printmovie (movies_t movie);
    ...
    

    就像其它数据类型一样,结构也可以有指针。其规则同其它基本数据类型一样:指针必须被声明为一个指向结构的指针:

    struct movies_t {
        char title [50];
        int year;
    };
    movies_t amovie;
    movies_t * pmovie;//等价于pmovie = &amovie;
    

    操作符->常与结构或类的指针一起使用,以便引用其中的成员元素,这样就避免使用很多括号。例如:

    pmovie->title
    

    来代替:

    (*pmovie).title
    

    以上两种表达式都是合法的,都表示取指针pmovie 所指向的结构其元素title 的值。
    结构可以嵌套使用,即一个结构的元素本身又可以是另一个结构类型。

    struct movies_t {
        char title [50];
        int year;
    }
    
    struct friends_t {
        char name [50];
        char email [50];
        movies_t favourite_movie;
    } charlie, maria;
    
    friends_t * pfriends = &charlie;
    

    因此,在有以上声明之后,我们可以使用下面的表达式:

    charlie.name
    maria.favourite_movie.title
    charlie.favourite_movie.year//等价于pfriends->favourite_movie.year
    

    本文中所讨论的结构的概念与C语言中结构概念是一样的。然而,在C++中,结构的概念已经被扩展到与类(class)相同的程度,只是它所有的元素都是公开的(public)

  3. 自定义数据类型 (User defined data types)
    typedef
    C++ 允许我们在现有数据类型的基础上定义我们自己的数据类型。用关键字typedef来实现这种定义,它的形式是:

    typedef existing_type new_type_name;//将已存在的类型定义成新类型。
    

    如果在一个程序中反复使用一种数据类型,而在以后的版本中有可能改变该数据类型的情况下,typedef 就很有用了。或者如果一种数据类型的名称太长,用一个比较短的名字来代替,也可以用typedef。
    联合(Union)
    使得同一段内存可以被按照不同的数据类型来访问,数据实际是存储在同一个位置的。它的声明和使用看起来与结构(structure)十分相似,但实际功能是完全不同的:

    union model_name {
        type1 element1;
        type2 element2;
        type3 element3;
        .
        .
    } object_name;
    

    union 中的所有被声明的元素占据同一段内存空间,其大小取声明中最长的元素的大小。
    每一个是一种不同的数据类型。它们都指向同一段内存空间,改变其中一个元素的值,将会影响所有其他元素的值
    在 C++ 我们可以选择使联合(union)匿名。如果将一个union包括在一个结构(structure)的定义中,并且不赋予object名称 (就是跟在花括号{}后面的名字),这个union就是匿名的。这种情况下可以直接使用union中元素的名字来访问该元素,而不需要再在前面加 union对象的名称。

    struct {
        char title[50];
        char author[50];
        union {
            float dollars;
            int yens;
        };
    } book;
    -------
    book.dollars
    book.yens
    //域dollars 和yens 占据的是同一块内存空间,所以它们不能被用来存储两个不同的值。
    

    枚举(Enumerations)
    可以用来生成一些任意类型的数据,不只限于数字类型或字符类型,甚至常量true 和false。它的定义形式如下:

    enum model_name {
        value1,
        value2,
        value3,
        .
        .
    } object_name;
    --------------
    enum colors_t {black, blue, green, cyan, red, purple, yellow, white};
    ---------使用下面方式使用--------
    //创造了一种的新的数据类型,而它并没有基于任何已存在的数据类型:类型color_t,花括号{}中包括了它的 所有 的可能取值
    colors_t mycolor;
    mycolor = blue;
    if (mycolor == green) 
        mycolor = red;
    

    枚举数据类型在编译时是被编译为整型数值的,而它的数值列表可以是任何指定的整型常量 。如果没有指定常量,枚举中第一个列出的可能值为0 ,后面的每一个值为前面一个值加1。

    enum months_t { 
        january=1, february, march, april, may, june, july, august, september, october, november, december
    } y2k;
    

    在这个例子中,枚举类型months_t的变量y2k 可以是12种可能取值中的任何一个,从january 到 december ,它们相当于数值1 到 12,而不是0 到 11 ,因为已经指定 january 等于1。
    (疑惑:这样的设定有什么作用?)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值