c++ 复合类型

内容包括

  • 创建和使用数组
  • 创建使用C-风格字符串
  • 创建和使用string类型字符串
  • 使用方法getline() 和get()读取字符串
  • 混合输入字符串和数字
  • 创建和使用结构
  • 创建和使用共用体
  • 创建和使用枚举
  • 创建和使用指针
  • 使用new和delete管理动态内存
  • 创建动态数组
  • 创建动态结构
  • 自动存储 静态存储 和动态存储
  • vector 和 array类
4.1 数组
声明数组的通用格式:

typeName arrayName[arraySize]

例如:

int nums[12]
初始化规则:

只有定义时才能进行初始化,也不能将一个数组赋值给另一个数组

int cards[3] = {1,2,3}  // ok
int hands[3] // ok
hands[3] = {4,5,6} // not allowed
hands = cards // not allowed

初始化时,提供的数值可以少于数组长度,其他位置用0值填充。

初始化方法:
  • 初始化时可省略等号: int cards[3]{1,2,3};
  • 可不在大括号内包含任何东西,这将把所有元素都设置为零值;
  • 禁止初始化缩窄转换
long plifs[3] = {12,34,3.0} // no allowed covert float to long
char slifs[4] = {12,44444,'i'} // not allowed  44444 out of 8 byte
char tlifs[4] = {12,'a','b','c'} // allowed
4.2 字符串

C- 风格字符串具有一种特殊性质:以空字符为结尾,空字符写作’\0’其ASCII码为0,用来标记字符串的结尾。

   char dog[3] = {'d','o','g'};
   char cat[4] = {'c','a','t','\0'};
   char fish[5] = "fish";
拼接字符串常量:

C++ 允许拼接字符窜面值,即将两个用引号括起来的字符串合并在一起。任何两个由空白(空格 制表符和换行符)分隔的字符串常量都将自动拼接成一个,下面的所有输出语句都是等效的。

cout << "i have a dog " "and a cat\n";
cout << "i have a dog and a cat\n";
cout << "i have a dog "
"and a cat\n"; 
在数组中使用字符串:

要将字符串存储到数组中,常用两种方法:

  • 将数组初始化为字符串常量
  • 将键盘或文件出入读取到数组中
#include <iostream>
#include <cstring>
#define Size 30
using namespace std;

int main(int argnum,char *args[]) {

    char name1[Size];
    char out[Size] = "hello,your name?";

    cout << out << endl;
    cin >> name1;
    cout << "well, your name has " << strlen(name1) << " letters, and you have " << sizeof(name1) << " place to save it";

    return 0;
}
// 运行
hello,your name?
luslin
well, your name has 6 letters, and you have 30 place to save it
字符串输入:

上面的代码有缺陷,只是由于输入的原因被掩盖掉了,下面的例子:

#include <iostream>
#include <cstring>
#define Size 6
using namespace std;

int main(int argnum,char *args[]) {
    char name1[Size];
    char name2[Size];
    cout << "name1 ?" << endl;
    cin >> name1;
    cout << "name2 ?" << endl;
    cin >> name2;
    cout << "well, name1 is " << name1 << " , name2 is " << name2 << endl;
    return 0;
}
// 运行
name1 ?
lu lin
name2 ?
well, name1 is lu , name2 is lin

原因:cin 使用空白符(空格,制表符,换行符)来确定字符串的结束位置这意味着cin在取得’‘lu’'后将其放到name1中,再次获取时,发现了输入队列中的lin然后赋值给name2.

另一个问题是,输入字符可能比目标数组长,这将不能组织将一个长度30的字符串放到长度为10的数组中

getline() 每次读取一行字符串输入:

getline()函数每次读取一行。它通过换行符来确定行尾,但不保存换行符,在储存字符串时使用空白符来替换换行符

#include <iostream>
#include <cstring>
#define Size 6
using namespace std;

int main(int argnum,char *args[]) {
    char name1[Size];
    char name2[Size];
    cin.getline(name1,6);
    cin.getline(name2,6);
    cout << "well, name1 is " << name1 << " , name2 is " << name2 << endl;
    return 0;
}
// 运行
sdf h
sdf
well, name1 is sdf h , name2 is sdf

问题:

当输入字符串长度大于指定长度时,getline() 会设置失效位,并关闭掉后面的输入:

dsfasdf sadf
well, name1 is dsfas , name2 is
get()面向行的输入:

该函数有几种变体,其中一种变体的工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get()并不丢弃换行符,而是将其保留在输入队列中。假如我们两次调用get():

cin.get(name,6);
cin.get(name2,6); // problem

由于第一次没有将换行符取出,因此,第二次调用后看到的是换行符。如果不借助帮助,get()不能跨过换行符

正确的方法

#include <iostream>
#include <cstring>
#define Size 6
using namespace std;

int main(int argnum,char *args[]) {
    char name1[Size];
    char name2[Size];
    cin.get(name1,Size);
    while (cin.get() != '\n');
    cin.get(name2,Size);
    while (cin.get() != '\n');
    cout << "well, name1 is " << name1 << " , name2 is " << name2 << endl;
    return 0;
}
4.3 String 类简介:

要使用string类,必须在程序中包含头文件string。string类位于名称空间std中。

  • 可以使用C-风格字符串来初始化string对象
  • 可以使用cin输入到string,使用cout读取string
  • 可以使用数组表示法访问存储在string中的字符
int main(int argnum,char *args[]) {
    string name1;
    cin >> name1;
    cout << "well, name1 is " << name1 << " , title is " << name1[0] << endl;
    return 0;
}
4.3.1 c++11 字符串初始化:
    string n2 = {"asdf"};
    string n3 = "asdfasdf";
4.3.2 赋值、拼接和附加:
  • 不能将一个数组赋值给另一个数组,但可以将一个string对象赋值给另一个string对象。

  • 简化了合并操作,可以使用运算符+将两个string对象合并起来,还可使用 += 将字符串附加到string对象末尾。

  • 使用 string.size()获取字符串长度

  • 可以使用cin输入到string,使用cout读取string, 可以使用getline(cin,str)获取一行字符串


int main(int argnum,char *args[]) {
    string n1;
    string n2;
    getline(cin,n1);
    getline(cin,n2);

    cout << "well, n1 is " << n1 << " , n2 is " << n2 << endl;
    return 0;
}
4.4 结构简介:

定义结构:

struct person{
    string name;
    char age;
    bool sex;
};

int main(int argnum,char *args[]) {
    person pi {"lin",12, false};

    cout << "well, n1 is " << pi.sex << " , n2 is " << pi.age+1000 << endl;
    return 0;
}

属性:

  • 支持将列表初始化用于结构

  • c++ 使用用户定义的类型与内置类型相似。例如,可以将结构作为参数传递给函数,也可以让函数返回一个结构。还可以使用赋值运算符将结构赋值给另一个同类型的结构,这样结构中每个成员都将被设置成另一个结构中相应成员的值,即使成员是数组。这种赋值被成为成员赋值

  • 可以同时完成定义结构和创建结构变量的工作;

    struct person{
        string name;
        char age;
        bool sex;
    }p1,p2;
    
  • 可以创建元素为结构的数组,用法与数组相同。

  • 允许指定占用特定位数的结构成员,这使用创建与某个硬件设备上的寄存器对应的数据结构非常方便

struct person{
    string name;
    unsigned int age :8;	// 8 bits for age
    int :4;  // 4 bits unused
    int sex ;
}p1,p2;


int main(int argnum,char *args[]) {
     p1 = {"12",255, 1000};
    cout << "well, n1 is " << p1.sex << " , n2 is " << p1.age << endl;
    return 0;
}
4.5 共用体:

共用体(union)是一种数据格式,它能够储存不同的数据类型,但只能同时存储其中一种类型,用途之一是当数据使用两种或更多格式时,可节省空间

struct person{
    string name;
    unsigned int age :8;
    int type ;
    union id {
        long id_num;
        char id_char[20];
    }id_val;
    int sex ;
}p1,p2;

int main(int argnum,char *args[]) {
     p1 = {"12",255, 1,22,0};
    cout << "well, n1 is " << p1.id_val.id_num << " , n2 is " << p1.age << endl;
    return 0;
}

匿名共用体没有名称,其成员将成为位于相同地址的变量。显然,每次只有一个成员是当前成员

struct person{
    string name;
    unsigned int age :8;
    int type ;
    union  {
        long id_num;
        char id_char[20];
    };
    int sex ;
}p1;
 cout << "well, n1 is " << p1.id_num 
4.6 枚举:

C++的enum工具提供了另一种创建符号常量的方式,这种方式可以代替const。他还允许定义新类型。

enum color {red,blue,yellow,white,black,green}
  • color为新的类型名称,称为枚举,red、blue等作为符号常量,对应整数值0~7.这些常量称为枚举常量
  • 在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举常量赋值给这种枚举的变量。
4.7 指针和只有存储空间:
  • 声明和初始化指针:

    int *p1;
    int main(int argnum,char *args[]) {
        int i = 12;
        int * j;
        j = &i;
        cout << j << *j << endl;
        return 0;
    }
    
  • 危险:

    在创建指针时,计算机将分配用来存储地址的空间,但没有分配用来存储指针所指向数据的内存,这种错误隐蔽,不易发现。

      int * j;
      *j = 12;
    
  • 指针和数字:

    指针不是整型,虽然计算机通常把地址当做整数来处理。但从概念来说,指针与整数是截然不同的类型。整数是可以执行加减乘除的数字,而指针描述的是位置,将两个地址相乘没有意义。要将数字当做指针使用,需要通过强制转换

    int *j;
    j = (int *) 0xB8000000;
    
  • 使用new来分配内存:

    int *j = new int;
    *j = 123;
    
  • 使用delete 释放内存:

    它使得在使用完内存后,能够将其归还给内存池,这是通向最有效使用内存的关键一步。归还或释放的内存可供程序的其他部分使用。使用delete时,后面要加上指向内存块的指针

    int *j = new int;
    ....
    delete j;
    

    只能用delete来释放使用new分配的内存。然而对空指针使用delete是安全的;一般来说,不要创建两个指向同一内存块的指针,因为这将增加错误的删除同一个内存块两次的可能性

  • 使用new创建动态数组:

        int *j = new int;
        *j = 123;
        int *i = new int[*j];
        delete []i;
        delete j;
    

    总之,使用new和delete时要遵循一下原则:

    • 不要使用delete来释放不是用new创建的内存
    • 不要使用delete释放同一内存块两次
    • 如果使用new[] 创建,要使用delete[]释放
    • 对空指针使用delete是安全的
  • 使用动态数组

    创建的指针指向的是动态数组的第一个元素

    int * j = new int[10]
    

    这里 *j 指的就是第一个数据, *(j + 1) 或 j[2] 指第二个数据

  • 指针与数组等价的基本原因在于指针算术和c++内部处理数组的方式。当指针变量增加1时,增量等于指针指向的数据类型的字节数,多数情况下,C++将数组名解释为数组第一个元素的地址

  • 创建动态结构时,使用- > 访问成员变量;

    struct person {
        string name;
        int age;
    };
    int main(int argnum,char *args[]) {
        person * p1 = new person;
        p1->age = 12;
       }
    
4.8 数组的替代品:
  • 模板类 vector:

    模板类vector类似于string类,也是一种动态数组。可以在运行期间设置vector对象的长度,可以在末尾附加新数据,可以在中间插入新数据。

    要使用vector类需要包含头文件vector,命名空间std

    vector <int> ps;
    
  • vector 的功能强大,但付出的代价是效率低下,如果使用的是固定长度的数组,可以使用array类,它效率与数组相同,但是更加安全、方便,需要头文件array,命名空间std

    array <int,12> as = {1,2,3,4,4};
    cout << as.at(7);
  • 无论是vector、array 还是数组,都可以使用标准数组表示法来访问各个元素。从地址区分,array和数组对象存储在相同的内存区域(栈)中,而vector对象存储在堆或自由存储区中。最后array对象可以赋值给另一个对象,但是对于数组而言,必须逐元素复制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值