2.1 基本内置类型
建议:如何选择类型
- 明确知晓该数不能为负数时,选用无符号类型。
- 使用int 执行整数运算。 实际中,short 太小,long和int 差不多,。如果超过int 表示的范围,选用long long。
- 在算术表达式中,不用char 和bool。
- 执行浮点数运算时,选用double类型。因为float 精度不够,且double 运算速度更快。
2.1.2 类型转化:
unsigned char c = -1; // 假设char占8bit, c值为255
signed char c2 = 256; // 假设char占8bit, c2值是未定义的
- 当赋值给无符号类型,超过它的表示范围值时,结果是初始值对无符号类型表示数值总数取模后的余数。 即-1对256取模后的数值255
- 当赋值给 带符号类型,超出它的表示范围值时,结果是未定义。
2.1.3 字面值常量
- 一个型如42的值被称为字面值常量。
20 /* 十进制*/
024 /* 八进制*/
0x /* 十六进制*/
- 字符串太长时,可以分开书写
cout<<" a really, really long string literal "
" that spans two lines" <<endl;
2.2.2 变量声明和定义的关系
- c++ 采用分离式编译机制,该机制允许将程序分割为几个文件,每个文件可以被独立编译。
- 声明 使得名字为程序所知,一个文件如果想使用别处定义的名字,则必须包含那个名字的声明,规定了变量的类型和名字; 定义 负责创建于名字关联的实体,申请存储空间,可能会赋值。
- 如果只是想声明,而不是定义,就在变量前面添加extern:
extern int i; // 声明 i, 而不是定义i
int j; // 声明并定义j
- 变量只能被定义一次,但可以被多次声明。
- C++ 是静态类型语言,即在编译阶段检查类型。
2.3.1 引用
- 引用即别名。因为引用本身不是一个对象,所以不能定义引用的引用。
- 引用必须初始化。因为引用是别名,必须先有对象,初始化即对象。
- 引用类型的初始值必须是一个对象。
- 定义引用时,要将引用和它的初值绑定在一起,而不是将初始值拷贝给引用。
2.3.2 指针
- 指针本身是一个对象,允许对指针赋值和拷贝;指针无须再定义时赋初值。
- 指针存放某个对象的地址,使用取地址符
int ival =42;
int *p = &ival ; //p存放变量ival 的地址,p指向变量val的指针。
cout << *p <<endl; //输出42
- 空指针不指向任何对象。
int *p1 = nullptr; // 等价于 int *p1 = 0;
int *p2 = 0; // 直接将p2 初始化为字面常量0
int *p3 = NULL; // 等价于 int *p1 = 0; 包含头文件 #inclde cstdlib
2.3.3 复合类型说明
- 指向指针的指针
int ival = 1024;
int *ip =&ival; //
int **ppi = &ip; //
int * &r = ip; //r 是 一个ip的别名
说明:ppi–>ip–>ival
2.4 const 限定符
- const是一种变量。这种变量值不能被改变。 例如设置缓存区的大小,可以防止该值不小心被改变掉。
const int bufSize = 512;
- const 对象一旦创建就不能再改变其值,所以const 对象必须初始化。
- 编译器在编译时,把代码中所有用到bufSize的地方,全部替换成对应的值。
- const对象仅在文件内有效,当多个文件中出现了同名的const 变量时,其实等同于在不同的文件中分别定义了独立的变量。
- 实现文件共享,可以只在一个文件中定义const, 而在其他多个文件中声明并使用它
// file_1.cpp 定义并初始化了一个常量,该常量能够被其他文件访问
extern const int bufsieze = fcn();
// file_1.h 头文件
extern const int bufsize ; //与file_1.cpp 定义的是同一个
- file_1.h 中的extern 说明bufsieze 并非本文件所独有,可以在其他文件中定义并且使用它。
2.4.1 const 的引用
2.5 处理类型
2.5.1 类型别名
typedef double wages; // wages 是double 的同义词
typedef wages base, *p; //base是double的同义词,p是double *的同义词
2.6 自定义数据结构
2.6.1 定义Sales_data 类型
struct Sales_data{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
}, accum; //一定要加上分号
2.6.2 使用 Sales_data 类
2.6.3 编写自己的头文件
- 类通常定义在头文件中,并且类名与所在的文件名一致。例如 库类型string 在名为string的头文件中。
- 头文件中也常用到其他头文件的功能。例如 编写的Sales_data.h文件中用到了string.h头文件。
- 当多次包含同一个头文件时,要使用 #pragma once。 例如本程序中,在主程序Sales_data.cpp中,包含了头文件 string; 而在Sales_data.h 中又再次隐含了头文件string。
#pragma once
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include "Version_test.h"
#include <string>
struct Sales_data {
std::string bookNo;
#ifdef IN_CLASS_INITS
unsigned units_sold = 0;
double revenue = 0.0;
#else
unsigned units_sold;
double revenue;
#endif
};
#endif
- 说明:当第一次包含Sales_data.h 时,#ifndef 的检查结果为真,预处理器顺序执行后面的操作,知道遇到程序最后面的语句 #endif。
- 此时,预处理变量 SALES_DATA_H 的值将变为已定义,而且 Sales_data.h 也会被拷贝到程序中。
- 后面再次包含 Sales_data.h ,则 #ifndef 的检查结果为假,编译器将忽略#ifndef… endif 之间的部分。
- 同样,在库中的头文件string也已经使用了这种预处理功能,所以不会出现重复声明和定义的问题。
- #pragma once 其实与#ifndef 的功能相同,只是这里是为了解决文件中的提示问题,可以参考文件 http://blog.sina.com.cn/s/blog_65b4bf590101hewq.html
- 预处理变量大写的原因是 为了避免与程序中的其他对象实体发生冲突。
# include <iostream>
using namespace std;
# include <string>
#include "Sales_data.h"
// 自己实现输入,输出,相加功能
int main()
{
Sales_data data1, data2;
// 读入 data1 和data2 的代码
double price = 0.0 ; //书的单价,用于计算销售的收入
// 读入第 1 笔交易 : ISBN, 销售数量、单价
cin >> data1.bookNo >> data1.units_sold >> price;
// 计算销售收入
data1.revenue = data1.units_sold * price;
// 读入第 2 笔交易 : ISBN, 销售数量、单价
cin >> data2.bookNo >> data2.units_sold >> price;
// 计算销售收入
data2.revenue = data2.units_sold * price;
// 检查 data1 和data2 的ISBN 是否相同
if (data1.bookNo == data2.bookNo)
{
unsigned totalCnt = data1.units_sold + data2.units_sold;
double totalRevenue = data1.revenue + data2.revenue;
// 如果相同,求data1 和data2 的总和
cout << data1.bookNo << " " << totalCnt
<< " " << totalRevenue << " " ;
if(totalCnt != 0)
cout << totalRevenue/totalCnt <<endl;
else
cout << " No sales" <<endl;
return 0;
}
else
{ //ISBN 不相同
cerr << " Data must refer to the same ISBN" <<endl;
return -1;
}
}