零基础学C++,自定义数据类型
C++基础学习(DAY_07)
- 9.1 结构类型
- 9.2 联合类型
- 9.3 枚举类型
- 9.4 命名空间
- 9.5 多文件组织
9.1 结构类型
结构属于自定义类型的一种,可以定义一些符合的数据结构,用来存储复杂数据。
比如:存储一个学生的基本信息,包括了很多信息,如:学号、姓名、性别、年龄等。不能用数组存储
可以使用结构体;来保存;
定义:
struct 结构名
{
数据类型1 成员变量名1;
数据类型2 成员变量名2;
…
数据类型n 成员变量名n;
};
使用:
结构名 结构变量名;//这样来定义一个结构变量
结构变量名.成员变量名;//可以访问结构变量中的每个成员变量
struct student
{int no;//学号char gender;//性别int age;double score;
};void test01()
{ //定义一个结构变量student s1;//访问结构变量中的成员变量,并且初始化s1.no = 2025100;s1.gender = 'm';s1.age = 20;s1.score = 90.5;cout << "成绩:" << s1.score << endl;cout << "学号:"<< s1.no << endl;cout << "性别:"<< s1.gender << endl;cout << "年龄:"<< s1.age << endl;//cout << "成绩:"<< s1.score << endl;//字节对齐cout << "student结构体大小:" << sizeof(student) << endl;//16字节//可见结构体的大小并不完全等于其内部每个成员变量类型的大小之和,这是因为字节对齐造成的。//字节对齐:是编译器自动完成的,目的是提高数据的访问效率。//基本规则:结构体的成员变量按照声明顺序依次存储,并且每个成员变量都有自己的对齐要求,即起始地址是其数据类型大小的整数倍。//查看每个数据成员变量的起始地址cout << "学号:" << &s1.no << endl;cout << "性别:" << (void*) & s1.gender << endl;cout << "年龄:" << &s1.age << endl;cout << "成绩:" << &s1.score << endl;//结论:结构体的总大小,一定是其中最大的数据类型的整数倍。
}
结论:结构体的总大小,一定是其中最大的数据类型的整数倍。
- 结构体类型的初始化和嵌套:
结构体初始化可以支持统一初始化方式{}
结构体中还可以包含其他结构体,称为嵌套
struct date//代表某个人的出生日期
{int year;int month;int day;
};struct Person
{int id;char gender;date birthday;//出生日期
};void test02()
{Person p = { 2025200,'m',{2013,5,10} };cout << "学号:" << p.id << endl;cout << "性别:" << p.gender << endl;cout << "出生日期:" << p.birthday.year<< "-" << p.birthday.month << "-" << p.birthday.day << endl;
}
- 结构数组与结构指针
结构数组:由结构体变量组成的数组。语法:结构体类型 数组名[常量表达式];
结构指针:指向结构体的指针。语法:结构体类型 *指针名;
void test03()
{//想保存多个学生信息,就用到结构数组student s_arr[3] = { {2025110,'m',21,92},{2025111,'m',22,93},{2025112,'f',20,87}};float sum = 0;for (size_t i = 0; i < 3; i++){sum += s_arr[i].score;}cout << "平均成绩:" << sum / 3 << endl;
}
//用结构指针传递结构数组,写函数来求平均成绩,函数第一个参数是结构指针,第二个代表数组中的元素个数
float avgscore(student* p,int n)
{float sum = 0,avg = 0;for (size_t i = 0; i < n; i++){sum += p[i].score;}avg = sum / n;return avg;
}void test04()
{student s_arr[5] = { {2025110,'m',21,92},{2025111,'m',22,93},{2025112,'f',20,87} ,{2025113,'f',25,100},{2025114,'m',23,100} };cout << "平均成绩:" << avgscore(s_arr,5) << endl;}
9.2 联合类型
联合属于一种自定义类型,特点是所有的成员变量共用一块内存。也叫共用体。
联合在任意时刻,只有一个成员变量是有实际值。其他的成员是没有意义的值。
目的:节省内存空间,同时实现多个成员选一个的效果。
定义格式:
union 联合名
{
数据类型1 成员变量名1;
数据类型2 成员变量名2;
…
数据类型n 成员变量名n;
};
注意:联合体的大小等于其成员中最大的类型大小。
union my_data
{int i;char ch;double f;};void test05()
{my_data d;d.i = 97;cout << d.i << "," << d.ch << "," << d.f << endl;//只有i有实际的值,其他没有实际值,没有意义d.ch = 'A';cout << d.i << "," << d.ch << "," << d.f << endl;//只有ch有实际的值,其他没有实际值,没有意义d.f = 3.14;cout << d.i << "," << d.ch << "," << d.f << endl;//只有f有实际的值,其他没有实际值,没有意义//联合体大小等于最大的类型cout << sizeof(my_data) << endl;cout << sizeof(d) << endl;
}
9.3 枚举类型
枚举型:
这是一个用户自定义的类型,可以用一组命名的常量来表示特定的值,这样可以增强代码的可读性,维护性。举例如下:
//自定义一个枚举类型 表示星期
enum Weekday
{//枚举常量,默认情况下,第一个枚举常量是0,后面依次+1Mon, Tue, Wed, Thu, Fri, Sat, Sun
};
//可以为枚举类型中的常量指定特定的值
enum Gender
{MAN = 1, WOMEN = 0
};
9.4 命名空间
程序大了之后会分成很多子项目,由不同的人开发维护,所以会带来标识符冲突的问题。
所以,C++中通过命名空间来避免这个问题。通过不同的命名空间来指定,就可以使用相同的标识符。
==定义:==命名空间只能在全局范围定义。
namespace 命名空间名
{
这个命名空间下的代码;
}
使用:
命名空间名::标识符;
声明:
1)单独声明: using 命名空间名::标识符名;
使用using声明要使用哪个命名空间的哪个标识符
有声明之后,就不需要在使用某个标识符时,在前面添加命名空间名了。
2)整体声明: 使用using namespace 命名空间名;
表示要使用整个命名空间下的所有标识符
单独声明:
namespace namespace1
{int fun_add(int x, int y) { return x + y; };
}namespace namespace2
{int fun_sub(int x, int y) { return x - y; };
}namespace namespace3
{int fun_mul(int x, int y) { return x * y; };
}
//单独声明
using namespace1::fun_add;
using namespace2::fun_sub;
void test07()
{//这里直接使用声明的标识符,不需要加命名空间名cout << fun_add(3,4) << endl;cout << fun_sub(3,4) << endl;//cout << fun_mul(3,4) << endl; 不能直接使用没有声明的标识符
}
整体声明:
//整体声明
namespace calc_ns
{int fun1(int x, int y) { return x + y; }int fun2(int x, int y) { return x - y; }int fun3(int x, int y) { return x * y; }
}using namespace calc_ns;void test08()
{//可以直接使用calc_ns下面的所有标识符了cout << fun1(3, 4) << endl;cout << fun2(3, 4) << endl;cout << fun3(3, 4) << endl;
}
9.5 多文件组织
前面写的程序都是在一个文件中完成,但实际项目中会有大量的程序文件和其他资源,这些文件放在不同的目录下。
代码密切相关的文件有:头文件(.h)和源文件(.cpp)
==头文件:==一般存放的是结构或者类的声明、函数的原型声明、全局变量、常量等。
==源文件:==存放的是函数功能的实现。
编译前的预处理指令:
预处理实在编译之前的处理工作,需要预处理器完成。预处理指令都是以#开头,并且不能加分号结束,不属于C++代码;
预处理的主要任务有:
1)文件包含
借助include指令把指定的头文件内容嵌入到源文件中。
如:#include <iostream> 适用于系统指定目录中的头文件<>
#include “Point.h” 适用于自己定义的头文件,要用"",他会默认在当前源文件的同级目录下寻找
做文件包含的时候,最怕的是文件被重复包含,因为重复包含会导致错误。
所以,Virtual Studio在创建头文件时会自动添加一行预处理代码#pragma once,防止被重复包含。
但是,#pragma once只在Windows平台得到支持,如果换成如linux等其他平台,就不支持。
有一种适用于所有平台的跨平台通用方式来解决文件重复包含问题,就是使用条件编译。
#ifndef指令:防止头文件被重复包含,作用是检查某个头文件是否未定义,如果未定义,就定义这个头文件,否则就不定义。
注意:#ifndef后面的命名方式为:头文件名全部大写_H
#ifndef POINT_H
#define POINT_Hstruct Point
{int x;int y;
};#endif // !POINT_H
2)宏定义
使用#define指令来定义宏常量,从而实现在源文件中的替换。
如:#define PI 3.14
3)条件编译
根据条件来决定是否编译某些代码块。
常用的指令有:#ifdef、#ifndef 、#endif等;
#ifdef :用于检查某个宏是否已定义,如果已定义,就编译#ifdef后面的代码,如果未定义就编译#else后面的代码,最后以#endif结束
#pragma once
#include <iostream>
using namespace std;
#define DEBUG 0void debug()
{//使用条件编译
#ifdef DEBUGcout << "DEBUG宏已定义" << endl;
#else cout << "DEBUG宏未定义" << endl;
#endif//DEBUG
}