数据类型
文章目录
整型
int(整型)
- Int8, 等于Byte, 占1个字节.
- Int16, 等于short, 占2个字节. -32768 32767
- Int32, 等于int, 占4个字节. -2147483648 2147483647
- Int64, 等于long, 占8个字节. -9223372036854775808 9223372036854775807
typedef定义的别名
typedef unsigned int uint32_t;//_t意味着是typedef定义
typedef unsigned long uLint32_t;
typedef unsigned long long uint64_t;
typedef unsigned short int uint16_t;
---------------------------------------------------
#ifndef __int8_t_defined
# define __int8_t_defined
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
#endif
---------------------------------------------------
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
#ifndef __uint32_t_defined
typedef unsigned int uint32_t;
# define __uint32_t_defined
#endif
#if __WORDSIZE == 64
typedef unsigned long int uint64_t;
#else
__extension__
typedef unsigned long long int uint64_t;
#endif
*size_t 和 int 比较
**size_t在32位架构中定义为:
typedef unsigned int size_t;
**size_t在64位架构中被定义为:
typedef unsigned long size_t;
**size_t是无符号的,并且是平台无关的,表示0-MAXINT的范围;int为是有符号的;
**int在不同架构上都是4字节,size_t在32位和64位架构上分别是4字节和8字节,在不同架构上进行编译时需要注意这个问题。
**ssize_t是有符号整型,在32位机器上等同与int,在64位机器上等同与 long int.
浮点型
浮点数
大小比较
- 小数比较大小要确定精度来进行(一般精确最少要到小数点后六位,更精确更好)
字符型
布尔型
string
#include <string>
find()
*string中find()返回值是字母在母串中的下标位置。
如果没有找到,那么会返回一个特别的标记npos,一般写作string::npos。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin>>str;
//主要字符串是从0开始计数的
cout<<"ab在str中的位置:"<<str.find("ab")<<endl;
//返回第一次出现ab的位置,没有则返回一串乱码
cout<<"ab在str[2]到str[n-1]中的位置:"<<str.find("ab",2)<<endl;
//返回第一次从str[2]开始出现ab的位置,没有则返回一串乱码
cout<<"ab在str[0]到str[2]中的位置:"<<str.rfind("ab",2)<<endl;
//返回ab在str[0]到str[2]中的位置,没有则返回一串乱码
s.find(str,pos);
find(str,pos);//是用来寻找从pos开始(包括pos处字符)匹配str的位置。
return 0;
}
输入1
sdefdwefdadabbababab
1
输出1
ab在str中的位置:11
ab在str[2]到str[n-1]中的位置:11
ab在str[0]到str[2]中的位置:18446744073709551615
Program ended with exit code: 0
输入2
abfeofihwabab
输出2
ab在str中的位置:0
ab在str[2]到str[n-1]中的位置:9
ab在str[0]到str[2]中的位置:0
Program ended with exit code: 0
输入3
asdfghjk
输出3
ab在str中的位置:18446744073709551615
ab在str[2]到str[n-1]中的位置:18446744073709551615
ab在str[0]到str[2]中的位置:18446744073709551615
Program ended with exit code: 0
substr()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin>>str;
cout<<"返回str[3]及其以后的子串:"<<str.substr(3)<<endl;
//若小于限制长度则报错
cout<<"从str[2]开始由四个字符组成的子串:"<<str.substr(2,4)<<endl;
//若小于限制长度则报错,4是**字符数**
return 0;
}
输入1
asdfghjkl;'/.,mnbvcxz
输出1
返回str[3]及其以后的子串:fghjkl;'/.,mnbvcxz
从ste[2]开始由四个字符组成的子串:dfgh
Program ended with exit code: 0
replace()
示列1
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line = "this@ is@ a test string!";
line = line.replace(line.find("@"), 1, ""); //从第一个@位置替换第一个@为空
cout<<line<<endl;
return 0;
}
输出
this is@ a test string!
Program ended with exit code: 0
示列2
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line = "this@ is@ a test string!";
line = line.replace(line.begin(), line.begin()+6, ""); //用str替换从begin位置开始的6个字符
cout << line << endl;
return 0;
}
示列3
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line = "this@ is@ a test string!";
char* str = "12345";
line = line.replace(0, 5, str); //用str替换从指定位置0开始长度为5的字符串
cout << line << endl;
return 0;
}
输出
12345 is@ a test string!
Program ended with exit code: 0
insert()
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str;
cin>>str;
cout<<"从2号位置插入字符串jkl并形成新的字符串返回:"<<str.insert(2, "jkl")<<endl;//会返回一个新的字符串,需要接收.
return 0;
}
输入
sdfgh
输出
从2号位置插入字符串jkl并形成新的字符串返回:sdjklfgh
Program ended with exit code: 0
append()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin>>str;
cout<<"在字符串str后面添加字符串ABC:"<<str.append("ABC")<<endl;
return 0;
}
输入
diguwhdcow
1
输出
在字符串str后面添加字符串ABC:diguwhdcowABC
Program ended with exit code: 0
swap()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1,str2;
cin>>str1>>str2;
cout<<"str1:"<<str1<<endl;
cout<<"str2:"<<str2<<endl;
swap(str1, str2);
cout<<"str1:"<<str1<<endl;
cout<<"str2:"<<str2<<endl;
}
输入
qwertyui
asdfghjk
输出
str1:qwertyui
str2:asdfghjk
str1:asdfghjk
str2:qwertyui
Program ended with exit code: 0
compare()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1,str2;
cin>>str1>>str2;
cout<<str1.compare(str2)<<endl;
return 0;
}
输入
diwguc
aschsdnv
输出
3
Program ended with exit code: 0
size()和length()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1;
cin>>str1;
cout<<str1.size()<<endl;
cout<<str1.length()<<endl;
return 0;
}
输入
dchiascnsc
输出
10
10
Program ended with exit code: 0
#include< string.h >
strcpy(s1,s2)
复制字符串s2到s1
strcat(s1,s2)
连接s2到s1的末尾
strlen(s1)
返回字符串s1的长度
strcmp(s1,s2)
若s1和s2是相同的,则返回0,s1< s2,返回值小于0,若s1>s2,返回值大于0
strchr(s1,ch)
返回一个指针,指向字符串s1中字符ch第一次出现的位置
strstr(s1,s2)
返回一个指针,指向字符串s1中字符串s2的第一次出现位置
memcpy (void *dest, const void *src, int size)
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
strcpy与memcpy的区别
- 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
- 复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
- 用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
指针类型
引用
引用的定义
*对变量起另外一个名字(别名),这个名字就成为该变量的引用。
int a;
int &ref_a = a;
**ref_a并没有在内存中开辟单元,只是引用a的单元。
**地址相同,但有两个名字。
*引用在被定义时,必须要初始化
*引用只能被定义一次,不可改成其他变量的引用。(唯一)
*常数值对应的引用必须是常引用
const double &rPI = 3.14;
引用和指针的区别
- 指针通过地址间接访问变量;引用通过别名直接访问变量。
- 存储指针需要额外的内存空间。
- 引用一旦被初始化,不会再作为其他变量的别名。
引用用作函数实参
//具有读写权限,而不是副本/拷贝,只可读。
//实参是变量而不是地址
void swap(int &x, int &y){
}
*常量引用
const int &x;//防止通过引用修改数值。
引用用作函数返回值
****warning****
int& m4(int x){
int &y = x;
return y;
}
指针的作用/用途
指针可以保存一个地址
指针的定义
-
数据类型 * 指针变量名
int * p;
-
让指针记录变量的地址
p = &a;
-
解引用
*p = 123213; //修改指向地址的数值
指针的内存空间
- 在32位操作系统下,占4个字节
- 在64位操作系统下,占8个字节
指针的地址
*区分指针的地址和指针所指向的地址
**已知int a,*pa=&a,输出指针pa 十进制的地址值的方法是cout<<long(&pa);
指针类型的分类
空指针
- 定义:指针变量指向内存中编号为0的空间
- 用途:初始化指针变量
- 注意:空指针指向的内存不可以访问(0~255之间的内存编号是系统占用的,不可以访问)
野指针
- 定义:指针变量指向非法的内存空间
空指针和野指针都不是我们申请的空间,不要轻易访问
智能指针
*智能指针有助于避免内存泄漏
*智能指针借助于引用计数来确定其所指向的对象的回收时机
*智能指针借助于操作符重载实现与普通指针类似的功能,包括-> 及 * 操作符
const修饰指针
const修饰指针 —常量指针
const int * p = &a;
**特点:**指针的指向可以修改,但是指针指向的值不可以修改
const修饰常量 —指针常量
int * const p = &a;
**特点:**指针的指向不可以改,指针指向的值可以改
const同时修饰指针和常量
const int * const p = &a;
**特点:**指针的指向不可以改,指针指向的值不可以改
指针和数组的结合
int arr[105];
int * const p = arr;
*因为数组的首地址不会更改,所以指针应用const修饰
*不用const修饰,可以用来遍历数组。
**int * p = arr; // arr是数组的首地址
**p++; // 指针向后偏移四个字节
*数组运算符优先级高于引用和指针
*指针数组
**存放指针的数组
int *arr[5];
*数组指针
**指向数组的指针
int(*arr)[5];
point (*p)[3];point是类名,p为定义指向对象数组的指针。
point *p[3];point是类名,p为对象指针数组 。
指针和函数的结合
*在说明语句int *f()中,标识符 f 代表是一个返回值为指针型的函数名。
*与 int (*f)() 不同
值传递
函数形参会发生改变,但是实参并没有发生改变
void max(int a){
}
max(a);
地址传递
void max(int *p){
}
max(&a);
总结
*int *p
int *p表示的是一级指针,表示p所指向的地址里面存放的是一个int类型的值。
一级指针存放变量的地址,指向的值是变量的内容。如int* p={1,2,3}, p=数组的首地址,*p=数组的第一个值;
一级指针通过形参,可以修改实参中指针所指向的地址中的值。修改不了实参中指针所指向的地址。需要借助二级指针才可以。
*int **p
int **p表示的是二级指针,表示p所指向的地址里面存放的是一个指向int类型的指针。
二级指针存放一级指针的地址,指向一级指针。如int *p ={1,2,3}, int **pp=&p,pp=指针p的首地址,*pp=数组的首地址,**pp=数组第一个值1。
指针数组的数组名传递参数对应为指针的指针即二级指针,二维数组的数组名传递参数对应为数组指针即指向一维数组的指针
*int (*p)()
简单的理解,这就可以理解为调用函数,其中(*p)这个整体可以看作是函数名称,p代表函数的入口地址。很简单的理解吗,c语言中()就代表了函数,所以把 *p用括号括在一起变作 (*p)就代表了一个函数。
*int *p()
为了便于理解,可以把这个p换成一个函数的名字 ,比如fun,那么int *fun()代表的是调用这个函数后,这个函数的返回值是一个指针。
*int *p[n]
int *p[n] 就是一个指针数组,数据类型为int ,元素为地址(变量地址,数组地址,函数地址等),也就是说定义了n个不同指向int型的指针。在字符优先级表中,[]的优先级大于*,所以,int *p[n] 就等价于int *(p[n])。
*int (*p)[n]
同上,根据优先级,int (*p)[n]表示定义一个指针,指向一个int[n]型的指针。
数组类型
- 数组大小
sizeof()
结构体类型
结构体变量的定义
- 只是一种数据类型,不占用内存空间;只有定义结构体变量时,才开辟内存空间。
- 将不同类型的数据有序地组合在一起,构成一个新的数据类型,称为结构体。
struct Student
{
int num;
char name[20];
char sex;
}s1,s2;
Student s3,s4; //可在之后声明添加.
--------------------
struct
{
int num;
char name[20];
char sex;
}s1,s2;//不可以在之后声明添加.
--------------------
struct Student
{
int num;
char name[20];
char sex;
Date birthday;
}s1;
struct Date
{
int month;
int day;
int year;
};
//赋值时
s1.birthday.day = 25;
*注意
**结构体成员名与程序变量名不冲突,如s1.name与局部变量name不冲突。
**不能对结构体变量整体输入和输出,只能分别使用各个成员。
***wrong cin>>s1; cout<<s1;
结构体变量的赋值
struct Student
{
int num;
char name[20];
char sex;
};
Student s1 = {123,"lihua",'M'}; //初始化
Student s2 = s1;
*注意
**s2 = s1;
***java
两者指向同一块地址,同时修改。
***c++
将s1复制给s2,两者之后的更改互不影响。
结构体数组
struct Student
{
int num;
char name[20];
char sex;
}stu[3] = {
{123,"lihua",'M'},
{123,"lihua",'M'},
{123,"lihua",'M'}
};
*每个数组元素都是一个结构体的值,在内存中连续存放。
结构体变量的指针
*指向运算符 ->
Student stu,*p;
p = &stu;
直接访问 | 间接访问 | 基于指向运算符的间接访问 |
---|---|---|
stu.name | p->name |
结构体变量传参给函数
*结构体变量名作为参数,将实参值传递给形参,一般较少使用这种方法
**时间和空间开销大,但要传入基本数据类型时,可以采用。
**结构体拷贝,效率不高。
void print (Student s){
cout<<s.name<<endl;
}
*指向结构体变量的指针作为实参,将结构体变量的地址传给形参。
**时间和空间开销小,效率高
//只有可读权限,同上。
void print (const Student *p){
cout<<p->name<<endl;
}
*用结构体变量的引用变量作为函数参数(推荐)
**高效
**安全
**可读性强
void print (const Student &s){
cout<<s.name<<endl;
}
自定义新类型
用typedef定义新类型
*typedef 已定义的类型 新的类型
typeof float real
real x,y;//等同于float x,y;
*作用:有利于程序的通用和移植。
//无论什么平台,real都表示最高精度小数。
#ifdef_win64
typedef long double real;
#else
typedef double real;
#endif
real r1,r2;
*typedef VS #define
**typedef float real:编译时处理,定义(复杂)数据类型。
**#define real float:预编译处理,所有real替换为float。
*复杂使用方法
typedef char STRING[81];
STRING s1;//char s1[81];
*应用
**图像,图形行业
double img1[1920][1080];
typedef double IMAGE[1920][1080];
IMAGE imag1;
进制
- Oct 八进制 写在数字左边cout<<oct<<25;
- Hex 十六进制
- 0+数字是八进制
- 0x+数字是十六进制
- 判断是否为十六进制,使用 isxdigit()
- 注意其返回的整数值,不同环境不一样,
奇怪!
常量
const
const修饰的变量称为常量。
const修饰的成员数据称为常数据成员
const修饰的成员函数称为常函数
函数的执行不会改变对象成员变量。getXXX()
const声明的对象为常对象。
对象的数据成员的值不被改变
Time const t1(12,34,46); //t1是常对象
常对象必须进行初始化,不能被更新(修改)
实型常量
C/C++中的实型常量必须符合一定的格式要求,包括小数点、指数符号等。如果不符合这些要求,就无法被编译器正确解析,因此是非法的实型常量。例如,以下几种实型常量都是非法的:
- “3…14”:有两个小数点,不符合实型常量的格式要求。
- “1.2E3.4”:有两个指数符号,不符合实型常量的格式要求。相当于12E-1E3.4。
- “0x1.2p3”:这是一个十六进制的实型常量,但是十六进制实型常量必须以小数点或指数符号开头,不符合格式要求。
因此,编程时应该注意实型常量的格式要求,避免出现非法的常量。
变量
局部变量
Main
True
String
string
printf
While
main
都可以用来命名
自增运算
++ ++x
自增两次++x++
会报错x++ ++
会报错(++x)++
自增两次
类型转换
字符串转整数
int i = stoi(string);
整型转换为字符串
-
to_string(int)
-
static_cast < type-id > ( expression ) 强制类型转换
-
(type-id)( expression )
double(a) + b / 2 //注意运算顺序
*数字的字符串形式转化为二进制字符串形式输出
cpp
string strToBinary(string str) {
int num = stoi(str);//stoi将字符串转化为整数
string binary_str;//空字符串binary_str,用于存储最终的二进制结果
while (num > 0) {
int last_bit = num & 1;//num & 1 与运算得到num的最后一位
binary_str = to_string(last_bit) + binary_str;
num >>= 1;//将num右移一位
}
return binary_str;
}
int main() {
string binary = strToBinary("255");
cout << binary << endl; // 11111111
}
----------------------------------------------------------------
cpp
string hexToBinary(string hex) {
int num = stoi(hex, 0, 16);
int binary_len = 8; // 设置二进制字符串长度为8位
string binary;
while (num > 0 || binary.length() < binary_len) {
if (num > 0) {
int last_bit = num & 1;
binary = to_string(last_bit) + binary;
num >>= 1;
} else { // 若num已转化完,则给binary补零
binary = "0" + binary;
}
}
return binary;
}
int main() {
string binary = hexToBinary("FF");
cout << binary << endl; // 11111111
}
-----------------------------------------------------------------
cpp
cout << setfill('0') << setw(8) << hex << endl;
//另一种补零方式
**字符串(十六进制)转变为整型(十进制)
//map
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
using namespace std;
bool is_little_endian(uint32_t n,int a,int b,int c,int d){
return (n == ((d << 24) | (c << 16) | (b << 8) | a));//位运算记得加括号
}
bool is_big_endian(uint32_t n,int a,int b,int c,int d){
return (n == ((a << 24) | (b << 16) | (c << 8) | d));
}
int hex_to_dec() {
string str;
cin>>str;
map<char, int> hex_map = {
{'0', 0}, {'1', 1}, {'2', 2}, {'3', 3},
{'4', 4}, {'5', 5}, {'6', 6}, {'7', 7},
{'8', 8}, {'9', 9}, {'a', 10}, {'b', 11},
{'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}
};
int sum = 0;
for (char c : str) {
sum = sum * 16 + hex_map[c];
}
return sum;
}
int main() {
for(uint32_t s; cin>>s;) {
int arr[4] = {0};
arr[0] = hex_to_dec();
arr[1] = hex_to_dec();
arr[2] = hex_to_dec();
arr[3] = hex_to_dec();
if(is_little_endian(s,arr[0],arr[1],arr[2],arr[3])){
cout<<"LE"<<endl;
}else if(is_big_endian(s,arr[0],arr[1],arr[2],arr[3])){
cout<<"BE"<<endl;
}else{
cout<<"UNKNOWN"<<endl;
}
}
return 0;
}
语法正确
;;;//;;;//正确
{{{ }}}//正确
((()))//错误
for(;;);//正确
do;while(0)//错误
do while(0);//错误
do;while(0);//正确
int a = 1, *p = &a; cout << (a/*p);//错误
类和对象
类
类的定义
*类的成员函数可以在类体中提供实现;但通常声明和实现分离。
**a.h
class A{
float x,y;
public:
void Setxy(float a,float b);
void Print(void);
};
**a.cpp
*成员变量属于对象,每个对象都有独立的成员变量。成员函数属于类,所有对象共享同一个成员函数。
对象的使用
- 类的变量称为对象。对象是类的实例。
- 类(对象)- “声明、实现、使用”
- 对象可选择内存空间——堆或栈,new出来的是在堆里。
*区别
(*p).hour
*p.hour
友元函数
*目的
让一个函数或者类访问另一个类中的私有成员
*关键字
friend
*注意
友元的关系是单向的而不是双向的,友元关系不能传递。
友元函数不一定是独立函数,也可以是另外一个类的函数。
函数作为友元函数
外部函数f(),在类A中用friend修饰声明,则f()为类A的友元函数,f可以访问A的所有成员。
friend void f();
//代码演示
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point类声明
public: //外部接口
Point(int x=0, int y=0) : x(x), y(y) { }//初始化
int getX() const{ return x; }//const,getX不能修改数据成员(x或y)
int getY() const{ return y; }
friend float dist(
const Point &a, const Point &b);
private: //私有数据成员
int x, y;
};
double dist( const Point& a, const Point& b) {
double x = a.x - b.x;
double y = a.y - b.y;
return sqrt(x * x + y * y);
}
int main() {
Point p1(1, 1), p2(4, 5);
cout <<"The distance is: ";
cout << dist(p1, p2)<<endl;
//OO风格,p1.dist(p2),略显奇怪
return 0;
}
#include <iostream>
#include <string>
using namespace std;
// 房屋类
class Building
{
// 告诉编译器 laoWang全局函数是 Building类 的好朋友,可以访问Building对象的私有成员
friend void laoWang1(Building *building);
friend void laoWang2(Building &building);
friend void laoWang3(Building building);
public:
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
//全局函数
void laoWang1(Building *building)
{
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}
void laoWang2(Building &building)
{
cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}
void laoWang3(Building building)
{
cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}
void test()
{
Building building;
laoWang1(&building);
laoWang2(building);
laoWang3(building);
}
int main()
{
test();
}
隔壁老王 全局函数 正在访问:(地址传递) 客厅
隔壁老王 全局函数 正在访问:(地址传递) 卧室
隔壁老王 全局函数 正在访问:(引用传递) 客厅
隔壁老王 全局函数 正在访问:(引用传递) 卧室
隔壁老王 全局函数 正在访问:( 值传递 ) 客厅
隔壁老王 全局函数 正在访问:( 值传递 ) 卧室
#include <iostream>
#include <string>
using namespace std;
class Building;
class LaoWang
{
public:
LaoWang();
void visit1(); //让visit1()函数 可以 访问Building中的私有成员
void visit2(); //让visit2()函数 不可以 访问Building中的私有成员
Building *building;
private:
};
class Building
{
// 告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
friend void LaoWang::visit1();
public:
Building();
string m_SittingRoom; //客厅
private:
string m_BedRoom; //卧室
};
LaoWang::LaoWang()
{
building = new Building;
}
void LaoWang::visit1()
{
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;
}
void LaoWang::visit2()
{
cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
//cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl; //错误!私有属性不可访问
}
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
void test()
{
LaoWang lw;
lw.visit1();
lw.visit2();
}
int main()
{
test();
return 0;
}
隔壁老王LaoWang类中的visit1()函数正在访问:客厅
隔壁老王LaoWang类中的visit1()函数正在访问:卧室
隔壁老王LaoWang类中的visit2()函数正在访问:客厅
类作为友元类
在类A中声明另一个类B声明为friend,则类A所有数据类B可以访问:friend class B;
#include <iostream>
#include <string>
using namespace std;
// 类作友元
class Building;
class LaoWang
{
public:
LaoWang();
void visit(); //参观函数 访问Building中的属性
Building * building;
private:
};
// 房屋类
class Building
{
// 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
friend class LaoWang;
public:
Building();
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
// 类外定义成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
LaoWang::LaoWang()
{
// 创建建筑物对象
building = new Building;
}
void LaoWang::visit()
{
cout << "隔壁老王LaoWang类正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类正在访问:" << building->m_BedRoom << endl;
}
void test()
{
LaoWang lw;
lw.visit();
}
int main()
{
test();
return 0;
}
隔壁老王LaoWang类正在访问:客厅
隔壁老王LaoWang类正在访问:卧室
面向对象
构造函数和析构函数
- 构造函数和析构函数是两种特殊的成员函数,作用是创建与释放对象。
- 构造函数:在创建对象时,负责将对象初始化(成员变量赋初值)。
- 任何类都会有构造函数,无论你是否主动定义
- 没有主动定义的情况下,编译器自动产生一个缺省构造函数。
class A{
float x,y;
public:
A(){ x = 0; y = 0;}
A(float a, float b = 10){
x = a;
y = b;
}
void Setxy(float a,float b);
void Print(void);
};
void main(){
//a4的定义方式相当于Java中A a = new a();
//a1后面不加()
A a1,a2(20.0),a3(3.0,7.0),*a4 = new A();
a1.Print();
a4->Print();
delete a4;//释放空间
}
*构造函数使用参数初始化表
//height,width是成员变量
Box::Box(int h,int w):height(h),width(w){}
//等同于
Box::Box(int h,int w){height = h; width = w;}
*委托构造函数:构造函数调用其他构造函数
Clock():Clock(0,0,0)//对应this(0,0,0)
*复制构造函数:被用于一个已存在对象初始化(赋值)另一个对象
A a1,a2 = a1;//等价于A a2(a1);
a2是a1的(数据)克隆。
**整体赋值通过复制构造函数,c++提供缺省的复制构造函数
**可以自己写复制构造函数。
class A{
float x,y;
public:
A(){ x = 0; y = 0;}
A(float a, float b = 10){
x = a;
y = b;
}
//复制构造函数
//const提供可读权限而不可以修改
//形参必须是同类型对象的引用
//浅拷贝
A(const A &a) {x = a.x; y = a.y;}
void Setxy(float a,float b);
void Print(void);
};
**浅拷贝
***缺省的复制函数,如果数据成员是指针,则复制函数拷贝指针,不拷贝指针指向的内存空间。
***使用new申请空间,通常需要自定义复制函数,实现深拷贝。
****原因:两个类,析构时,若是浅拷贝,new开辟的空间,会被释放两次,会造成运行错误。
**深拷贝
***将对象自身及关联的空间、对象都进行一边复制。
**被自动调用的三种情况
***定义一个对象时,以本类另一个对象作为初始值,发生复制构造;
//本质是构造,在创建对象时,会调用构造函数。
A a2 = a1;//与A a2; a2 = a1不一样,这是赋值,运算符的重载函数
***如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造;
int func(const A &a)//高效,没有发生复制,起了别名,是引用。
int func(A a)//克隆,低效
***如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造。
*析构函数在对象的生命期结束时,被系统自动调用,作用是收回为对象分配的储存空间。
*析构函数名必须是在类名前面加上字符"~"
ClassName::~ClassName(){...}
*析构函数不能带有任何参数,没有返回值,不指定函数类型。
*一个类只能定义一个析构函数,不允许重载。
*如果没有显式定义析构函数,则编译器自动产生一个缺省的析构函数,其函数体为空。
ClassName::~ClassName(){}
*不同对象的析构
**全局对象
**局部对象
**static定义的局部对象
**用new运算符动态生成的对象
*delete
**对于一个指针只能使用一次delete
**必须用于new返回的指针
**使用它删除对象时,要调用析构函数
static静态成员
class Point{
public:
Point(int x = 0,int y = 0):x(x),y(y){
count++;
}
Point (const Point &p){
x = p.x;
y = p.y;
count++;
}
~Point(){count--;}
int getX() const {return x;}//const:对于x只读,不会修改x的值。
int getY() const {return y;}
private:
int x,y;
static int count;//静态数据成员,初始值为0;
};
int Point::count = 0;//如果要对静态成员进行初始化,不能在类的内部。
运算符重载
*运算符重载:为运算符(+-*/><…)定义
一个(若干个)函数(运算符重载函数),遇到运算符根据不同的运算对象调用对应的函数
– <返回类型> operator <运算符>(<参数表>)
– operator是运算符重载函数关键字,
– operator <运算符>是函数名
*运算符的操作数映射为重载函数的参数
*运算符重载函数通常是类的成员函数或者是友元函数。函数参数通常是类的对象。
*(需要掌握的)重载的运算符
– 双目运算符 + - * / > < +=
– 单目运算符 ++ -- !
– 流运算符 >> <<
– 赋值运算符 =
– 类型转换函数
– (optional)其他运算符([]()等等,不常用)
• 转换构造函数,例如,int Integer
Class Vector{
Vector operator + (Vector &);
}
Vector v2, v3;
Vector v1 = v2 + v3;
v1 = v2.operator+(v3);
v1 = operator+(v3,v2);//友元函数
详见配套PPT-运算符重载
复数的运算符重载
//复数的运算符重载
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(double r=0, double i=0):real(r), imag(i){ }
Complex operator+(Complex &c) const;//重载双目运算符'+'
void operator-=(Complex &c); //重载双目运算符'-='
//Complex operator-=(Complex &c);
friend Complex operator-(Complex &c1,Complex &c2) const;//重载双目运算符'-'
void Display() const;
private:
double real;
double imag;
};
void Complex::Display() const{
cout << "(" << real << ", " << imag << ")" << endl;
}
Complex operator-(Complex& c1, Complex& c2) const{//const能够使调用该函数的对象的值不改变(只读)不加Complex::和friend
return Complex(c1.real - c2.real, c1.imag - c2.imag);
}
Complex Complex::operator+(Complex &c) const{//类内的函数加Complex::
return Complex(c.real + real,c.imag + imag);
}
void Complex::operator-=(Complex &c){
this->real -= c.real;
this->imag -= c.imag;
}
//Complex Complex::operator-=(Complex &c){
// this->real -= c.real;
// this->imag -= c.imag;
// return *this;最后返回的也是this指针所指的对象
//}
int main()
{
double r, m;
cin >> r >> m;
Complex c1(r, m);
cin >> r >> m;
Complex c2(r, m);
Complex c3 = c1+c2;
c3.Display();
c3 = c1-c2;
c3.Display();
c3 -= c1;
c3.Display();
return 0;
}
//分数计算
#include <iostream>
#include <cmath>
using namespace std;
class FS {
public:
FS(double fz=0, double fm=0):fz(fz), fm(fm) { }
FS operator+(FS &f) const;//重载双目运算符'+'
void Display() const;
private:
int fz;
int fm;
};
int gcd(int m,int n) {
int m_sec = max(m,n);
int n_sec = min(m,n);
//cout<<m_sec<<" "<<n_sec<<endl;
//cout<<1<<endl;
return m_sec % n_sec == 0 ? n_sec : gcd(n_sec,m_sec % n_sec);
}
FS simplify(int &fz,int &fm) {
//cout<<1<<endl;
if(fz == 0) {
return FS(0,1);
} else if(fm < 0 && fz < 0 || fm > 0 && fz > 0) {
fm = abs(fm);
fz = abs(fz);
int temp = gcd(fm,fz);
//cout<<temp<<endl;
return FS(fz / temp,fm / temp);
}else {
fm = abs(fm);
fz = abs(fz);
//cout<<fm<<" "<<fz<<endl;
int temp = gcd(fm,fz);
//cout<<temp<<endl;
return FS(-1 * (fz / temp),fm / temp);
}
}
FS FS::operator+(FS &f) const {
int fz_temp = fz * f.fm + f.fz * fm;
int fm_temp = fm * f.fm;
//cout<<2<<endl;
//cout<<fz_temp<<" "<<fm_temp<<endl;
FS fs = simplify(fz_temp,fm_temp);
//cout<<1.1<<endl;
fs.Display();
//cout<<1.2<<endl;
return fs;
}
void init(string s, int &m, int &n) {
int z_index = s.find("z");
string fz = "";
string fm = "";
for(int i = 0; i < z_index; i++) {
fz += s.at(i);
}
for(int i = z_index + 1; i < s.length() - 1; i++) {
fm += s.at(i);
}
//cout<<fz<<" "<<fm<<endl;
m = stoi(fz);
n = stoi(fm);
}
void FS::Display() const {
cout << fz << "z" << fm << "m" << endl;
}
int main() {
int n;
cin>>n;
for(int i = 0; i < n; i++) {
string s;
cin>>s;
int fz_one = 0;
int fm_one = 0;
//string -> int
init(s,fz_one,fm_one);
//cout<<fz_one<<" "<<fm_one<<endl;
int fz_two = 0;
int fm_two = 0;
cin>>s;
init(s,fz_two,fm_two);
FS fs_one(fz_one,fm_one);
FS fs_two(fz_two,fm_two);
FS fs_three = fs_one + fs_two;
}
return 0;
}
流运算符重载
cin
是类istream
对象、cout
是类ostream
的对象。- 流运算符
>>
和<<
是全局函数不能定义为类的方法
//istream& operator >> (istream& _cin, Object& obj);
//ostream& operator << (ostream& _cout, const Object& obj);
class Complex{
public:
Complex(){
real = 0;
imag = 0;
}
Complex(double r =0,double i = 0): real(r),imag(i) {};
friend ostream& operator<<(ostream& output, Complex& c);//通常情况下Object类的数据成员是private,要想让>>和<<操作其数据成员,需要在类的内部声明为这两个重载是友元,因为要改变cin,cout的状态,返回值是引用
private:
double real;
double imag;
}
ostream& operator<<(ostream& output, Complex& c){
output<<"("<<c.real<<"+"<<c.imag<<"i)"<<endl;
return output;
}
继承和派生
构造顺序
-
执行初始化列表
-
调用基类的构造函数,初始化基类对象。
-
按照成员变量的定义顺序,依次初始化。(包括类内的基类对象)
-
-
调用自身构造函数的实现部分
//对象指针
class Person {
Person() { // 构造函数
cout << "Constructing Person" << endl;
}
};
int main() {
Person p; // 调用构造函数,输出"Constructing Person"
Person *ptr; // 只是声明指针,不调用构造函数
ptr = &p; // 让指针指向对象,不调用构造函数
Person *ptr2 = new Person; // 调用构造函数,输出"Constructing Person"
// 因为此处创建了一个新对象
}
析构顺序
与构造顺序相反。(无static和全局变量)
多态
特殊函数
inline 内联函数
一些使用内联函数的注意事项:
- 不要将大的函数定义为内联函数,这可能导致代码膨胀而没有效率提高。
- 内联函数应该主要包含几行简短的代码。
- 只有当内联函数被调用很多次时,效率才会有明显提高。
- 虚函数不能定义为内联函数。
- 编译器有权忽略用户申请该函数变成内联函数的请求。
inline int add(int a, int b) {
return a + b;
}
int main() {
int sum = add(1, 2); // 展开为 int sum = 1 + 2;
// 等价于 int sum = 3;
}
//这里,add() 函数被指定为 inline,所以在调用 add(1, 2) 时,会直接将函数体展开,相当于写成了1 + 2。这避免了进行正常的函数调用和返回等操作。
常识
运算符
取余运算
-
a % b = a - (a / b) * b
-
C++ 中的 % 运算符结果的正负号和被除数 a 的正负号相同。
运算符优先级
流运算符
- 不等同于按位左(右)移运算符
- 比后置自增运算符 ++ 高。也就是说在同一行中,<< 会先于 ++ 执行。
- 比赋值运算符 = 低。所以在赋值语句中,会先计算右侧表达式,然后再执行 <<。
- 比条件运算符 ?: 低。所以在使用条件运算符时,会先计算条件表达式,然后再决定输出哪个运算对象。(要整体加括号)
- 比逻辑运算符 && 和 || 低。所以会先计算逻辑表达式的结果,然后再决定是否输出。
- 等价于关系运算符 >、<、>=、<=。所以比较运算和流输出的次序取决于从左到右的结合性。(加括号,否则会报错)
优先级 | 操作符 | 描述 | 例子 | 结合性 |
---|---|---|---|---|
1 | () [] -> . :: ++ – | 调节优先级的括号操作符 数组下标访问操作符 通过指向对象的指针访问成员的操作符 通过对象本身访问成员的操作符 作用域操作符 后置自增操作符 后置自减操作符 | (a + b) / 4; array[4] = 2; ptr->age = 34; obj.age = 34; Class::age = 2; for( i = 0; i < 10; i++ ) … for( i = 10; i > 0; i-- ) … | 从左到右 |
2 | ! ~ ++ – - + * & (type) sizeof | 逻辑取反操作符 按位取反(按位取补) 前置自增操作符 前置自减操作符 一元取负操作符 一元取正操作符 解引用操作符 取地址操作符 类型转换操作符 返回对象占用的字节数操作符 | if( !done ) … flags = ~flags; for( i = 0; i < 10; ++i ) … for( i = 10; i > 0; --i ) … int i = -1; int i = +1; data = *ptr; address = &obj; int i = (int) floatNum; int size = sizeof(floatNum); | 从右到左 |
3 | ->* .* | 在指针上通过指向成员的指针访问成员的操作符 在对象上通过指向成员的指针访问成员的操作符 | ptr->*var = 24; obj.*var = 24; | 从左到右 |
4 | * / % | 乘法操作符 除法操作符 取余数操作符 | int i = 2 * 4; float f = 10 / 3; int rem = 4 % 3; | 从左到右 |
5 | + - | 加法操作符 减法操作符 | int i = 2 + 3; int i = 5 - 1; | 从左到右 |
6 | << >> | 按位左移操作符 按位右移操作符 | int flags = 33 << 1; int flags = 33 >> 1; | 从左到右 |
7 | < <= > >= | 小于比较操作符 小于或等于比较操作符 大于比较操作符 大于或等于比较操作符 | if( i < 42 ) … if( i <= 42 ) … if( i > 42 ) … if( i >= 42 ) … | 从左到右 |
8 | == != | 等于比较操作符 不等于比较操作符 | if( i == 42 ) … if( i != 42 ) … | 从左到右 |
9 | & | 按位与操作符 | flags = flags & 42; | 从左到右 |
10 | ^ | 按位异或操作符 | flags = flags ^ 42; | 从左到右 |
11 | | | 按位或操作符 | flags = flags | 42; | 从左到右 |
12 | && | 逻辑与操作符 | if( conditionA && conditionB ) … | 从左到右 |
13 | || | 逻辑或操作符 | if( conditionA || conditionB ) … | 从左到右 |
14 | ? : | 三元条件操作符 | int i = (a > b) ? a : b; | 从右到左 |
15 | = += -= *= /= %= &= ^= |= <<= >>= | 赋值操作符 复合赋值操作符(加法) 复合赋值操作符(减法) 复合赋值操作符(乘法) 复合赋值操作符(除法) 复合赋值操作符(取余) 复合赋值操作符(按位与) 复合赋值操作符(按位异或) 复合赋值操作符(按位或) 复合赋值操作符(按位左移) 复合赋值操作符(按位右移) | int a = b; a += 3; b -= 4; a *= 5; a /= 2; a %= 3; flags &= new_flags; flags ^= new_flags; flags |= new_flags; flags <<= 2; flags >>= 2; | 从右到左 |
16 | , | 逗号操作符 | for( i = 0, j = 0; i < 10; i++, j++ ) … | 从左到右 |
ASCII
a | 97 |
---|---|
A | 65 |
常用值
int 2147483
c++源程序编译可执行程序的顺序
- 预处理:处理源程序中以 # 开始的预处理命令,比如 #include, #define 等。产生扩展源程序。
- 编译:将扩展源程序编译成汇编语言,产生汇编文件(.s 文件)。这一步主要进行词法分析、语法分析、语义分析。
- 汇编:将汇编语言文件汇编成目标文件(.o 文件),包含目标代码。
- 链接:将多个目标文件及库文件链接成可执行文件(或库文件)。这一步会进行符号解析、重定位等。
- 运行:运行最终生成的可执行文件。
头文件
#include <bits/stdc++.h>
#include <cassert>
assert
*作用是如果它的条件返回错误,则终止程序执行。
#include <assert.h>
void assert( int expression );
*每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败。
*assert的作用是先计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
#include <iostream>
#include <cstring>
-
memset(sum,0,sizeof(sum)); = int sum[10] = {0};
-
int arr[1005] = {0};
-
int arr[1005];//都可以
#include <string>
- S.at()获取单个字符
- s.length()获取字符串长度
- 判断字符串相等"=="
#include <algorithm>
- sort (起始地址, 末尾地址+1, cmp)
- swap 交换函数
- 可参考【C++】蓝桥杯必备 算法竞赛常用STL万字总结
#include <cmath>
- Sqrt()开平方根
- Pow( ,2)平方
- Abs()绝对值
#include <iomanip>
-
Setw() 指定显示字段的宽度(对其后面的一个起作用,每次都要设置)。默认右对齐setiosflags(ios:left)设置为左对齐
-
Setfill(‘字符’ )设置用于在右对齐显示中填充空格的字符。
-
cout<<fixed<<setprecision(2)<<输出保留两位小数
关键字
static
函数前加static,说明函数在整个cpp文件是可见的,但不可在其他cpp文件里调用。
extern
转义字符
\t
\t
对应空格次数并不是固定的,并不是我们认为的4个空格或者8个空格
令 num = |n-8|%8, 其中n表示\t前面的字符占的位置(前面的字符也可能是转换说明,如%d,%10d等)。
- 当\t前面为123456时,后面有两个空格
- 当\t前面为1234567时,后面有1个空格
- 而当前面正好为12345678时,后面跟8个空格
- 当前面为123456781234时,后面的空格数量为 |12-8|%8=4
错误总结
Dev C++
编译错误
- [[Error] ‘stoi’ was not declared in this scope]([(90条消息) [Error] ‘stoi’ was not declared in this scope_stoi函数用不了_疯狂理工~~的博客-CSDN博客](https://blog.csdn.net/android_mangren/article/details/84930764?ops_request_misc=%7B%22request%5Fid%22%3A%22168010198916800188571516%22%2C%22scm%22%3A%2220140713.130102334…%22%7D&request_id=168010198916800188571516&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-84930764-null-null.142v77insert_down38,201v4add_ask,239v2insert_chatgpt&utm_term=[Error] stoi was not declared in this scope&spm=1018.2226.3001.4187))
error: control may reach end of non-void function [-Werror,-Wreturn-type]
- 控制到达非void函数的结尾。就是说你的一些本应带有返回值的函数到达结尾后可能并没有返回任何值。这时候,最好检查一下是否每个控制流都会有返回值。
PTA
运行时错误
*s.at(i)//s == ""
*数组越界
疑惑
c++中 . 和 -> 有什么区别
-
.
是结构体变量或类的对象访问其成员。 -
->
是结构体指针或类的指针访问其成员。 -
在 C++ 中,this 是一个指向当前对象的指针。this 指针是动态绑定的,它总是指向调用当前成员函数的那个对象。
//两者等价
ptr->name
(*ptr).name
算法知识汇总
动态规划(DP)
实际生活应用
身份证号验证
输入
11010519491231002X
440524188001010019
输出
11010519491231002X: 1949-12-31 F
440524188001010019: invalid
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <sstream>
using namespace std;
int weg[17] = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
int last_num[11] = {1,0,10,9,8,7,6,5,4,3,2};
int month[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};//可改进
bool is_id_number(const string &s) {
if(s.length() != 18) return false;
for(int i = 0; i < s.length() - 1; i++) {
if(s.at(i) < '0' || s.at(i) > '9') {
return false;
}
}
int sum = 0;
for(int i = 0; i < s.length() - 1; i++) {
int num = (int)(s.at(i) - '0');
sum += weg[i] * num;
}
sum = sum % 11;
if(sum != 2) {
if(s.at(s.length() -1) < '0' || s.at(s.length() -1) > '9') {
return false;
}
if((int)(s.at(s.length() -1) - '0') == last_num[sum]) {
return true;
}
} else {
if((s.at(s.length() -1)) == 'X') {
return true;
}
}
return false;
}
bool judge_year(int year) {
if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ) {
return true;
} else {
return false;
}
}
void cout_sex(int identity) {
if(identity % 2 == 0) {
cout<<" F"<<endl;
} else {
cout<<" M"<<endl;
}
}
int main() {
for(string s; getline(cin, s);) {
cout<<s<<": ";
if(is_id_number(s) == true) {
int identity = 0;
string str_identity = "";
for(int i = 14; i <= 16; i++) {
str_identity += s.at(i);
}
identity = stoi(str_identity);
string year = "";
string month = "";
string day = "";
//year = stoi(substr(6,4));改进
for(int j = 6; j < 10; j++) {
year += s.at(j);
}
int Year = stoi(year);
month += s.at(10);
month += s.at(11);
int Month = stoi(month);
day += s.at(12);
day += s.at(13);
int Day = stoi(day);
bool flag_two = judge_year(Year);
if(flag_two == true && Month == 2) {
if(Day >= 1 && Day <= 29) {
cout << setfill('0') << setw(4) << Year <<"-"<< setfill('0') << setw(2) << Month <<"-"<< setfill('0') << setw(2) << Day;
cout_sex(identity);
continue;
} else {
cout<<"invalid"<<endl;
continue;
}
} else if(flag_two == false && Month == 2) {
if(Day >= 1 && Day <= 28) {
cout << setfill('0') << setw(4) << Year <<"-"<< setfill('0') << setw(2) << Month <<"-"<< setfill('0') << setw(2) << Day;
cout_sex(identity);
continue;
} else {
cout<<"invalid"<<endl;
continue;
}
}
if(Month > 12 || Month == 0) {
cout<<"invalid"<<endl;
continue;
}
if(Month == 1 ||Month == 3 || Month == 5 || Month == 7 || Month == 8 || Month == 10 ||Month == 12) {
if(Day >= 1 && Day <= 31) {
cout << setfill('0') << setw(4) << Year <<"-"<< setfill('0') << setw(2) << Month <<"-"<< setfill('0') << setw(2) << Day;
cout_sex(identity);
continue;
} else {
cout<<"invalid"<<endl;
continue;
}
} else {
if(Day >= 1 && Day <= 30) {
cout << setfill('0') << setw(4) << Year <<"-"<< setfill('0') << setw(2) << Month <<"-"<< setfill('0') << setw(2) << Day;
cout_sex(identity);
continue;
} else {
cout<<"invalid"<<endl;
continue;
}
}
} else {
cout<<"invalid"<<endl;
}
}
return 0;
}
选择排序
#include<iostream>
using namespace std;
void SelectSort(int A[],int n)
{
for(int i = 0;i < n;i++){
int index =i;
for(int j = i+1;j < n;j++){ //查找最大元素所在位置与index索引比较即可
if (A[j] > A[index])
index =j;
}
int temp = A[index]; //交换无序后列中首元素与最大元素的位置
A[index] = A[i];
A[i] = temp;
}
}
易错点总结
*字符串
**注意""
*日期类
**月份不能为零
**天数与月份对应,可用数组储存。
*数学
**PI的取法Acos(-1)