C++primer学习心得-第二章
文章目录
2.1 基本内置类型(包括算术类型(arithmetic)和空类型(void))
1. 算术类型
算术类型分为两类:整型(包括字符和布尔类型在内)和浮点型。
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 未定义 |
char | 字符 | 8位 |
wchar_t | 宽字符 | 16位 |
char16_t | Unicode 字符 | 16位 |
char32_t | Unicode字符 | 32 位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 长整型 | 32 位 |
long long | 长整型 | 64位 |
float | 单精度浮点数 | 6位有效数字 |
double | 双精度浮点数 | 10位有效数字 |
long double | 扩展精度浮点数 | 10位有效数字 |
bool类型的取值:true和false。
一个char的大小和一个机器字节一样。
除了布尔型和扩展的字符类型,其他整型可以划分为带符号的和无符号的(unsigned)两种。无符号类型的值必定大于0。通过在类型名前面添加unsigned就行了(unsigned long)。unsigned int可以简写为unsigned。
2. 类型转换
- 一个非布尔类型的算术值赋给布尔类型时,值为0则结果为false,否则为true
- 把布尔类型的值赋给非布尔类型时,false->0,true->1
- 浮点数赋给整数类型时会去掉小数部分
- 整数值赋给浮点类型时小数部分记为0
- 赋给无符号类型一个超出它表示范围的值时结果为初始值对无符号类型的表示数值总数取模后的余数
- 赋给带符号类型一个超出范围的值时,结果是未定义的(undefined)。
3. 字面值常量
-
对于整型字面值:八进制(0开头,如024)、十六进制(0x或0X开头,如0x14)和十进制
-
浮点型字面值表现为小数或者以科学计数法表示的指数如(0e0、3.14E0)
-
char型字面值为一个单引号括起来的一个字符(如’a’),双引号括起来的一个或多个字符构成字符串的字面值(如:“hello world”),字符串字面值实际上是常量字符构成的数组
-
有两类字符不可直接使用:不可打印字符(如退格或其他控制字符,没有可是图标)和c++中有特殊含义的字符(单双引号、问号、反斜杠)。这时就需要用到转义序列,c++语言规定的转义序列包括:
-
- 换行符 ->\n
- 横向制表符->\t
- 报警符(响铃)->\a
- 纵向制表符->\v
- 退格符->\b
- 双引号->\"
- 反斜杠->\\
- 问号->\?
- 单引号->\’
- 回车符->\r
- 进纸符->\f
转义字符被当作一个序列使用
-
指定字面值的类型:通过添加前缀或者后缀可以改变字面值的默认类型。
-
(字符和字符串字面值)前缀 含义 类型 u Unicode 16字符 char16_t U Unicode 32 字符 char32_t L 宽字符 wchar_t u8 UTF-8 char 后缀 最小匹配类型 整型字面值 u or U unsigned l or L long ll or LL l long long 后缀 类型 浮点型字面值 f or F float l or L long doube -
布尔字面值:true 和 falsee
2.2 变量
1. 变量的定义
类型说明符(type specifier) 后面紧跟一个或多个变量名组成的列表,逗号分隔,分号结束。当对象在创建时获得一个特定的值,我们称这个对象被初始化了。如我们要定义一个名为 i的int 变量并初始化为0,以下4条语句都可以实现:
- int i=0;
- int i={0}
- int i{0};
- int i(0);
用花括号来初始化的形式称为列表初始化。
如果定义变量时没有指定初始值,则变量会被默认初始化,变量会被赋予默认值。
2. 变量声明和定义的关系
声明 :在变量名前添加关键字extern且不要初始化变量,c++的分离式编译机制有关。
3. 标识符
- 有字母、数字和下划线组成,区分大小写
- 不能使用关键字和操作符替代名
- 要能体现实际含义
- 变量名一般用小写
- 类名首字母一般大写
- 由多个单词组成时最好用下划线分隔
4. 作用域(scope)
程序的一部分,在其中名字有特定的含义。
main函数定义于所有的花括号之外,和其他大多数定义于函数体外的名字一样具有全局作用域。
2.3 复合类型
复合类型指基于其他类型定义的类型。
1. 引用(reference)
引用为对象起了另外一个名字,引用类型引用另外一种类型。通过将声明符写成&d的形式来定义引用类型,d为变量名。
定义引用时程序把引用和它的初始值绑定在一起。定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。引用即别名。
int i=0,&ii=i;
2. 指针(pointer)
“指向”另一种类型的符合类型,指针也实现了对其他对象的间接访问,但不同于引用,指针本事是一个对象,允许对指针赋值和拷贝,可以先后指向不同的对象,无须在定义时赋初值。定义指针的方法将声明符写成*d的形式。
指针存放某个对象的地址,要想获取对象的地址可以用取地址符&:
int i=0;
int *p=&i;
指针的类型要与它指向的对象的类型严格匹配。
指针的值(地址)应属于下列4中状态:
- 指向一个对象
- 指向紧邻对象所占空间的下一个位置
- 空指针
- 无效指针
如果指针指向了一个对象,则允许使用解引用符*来访问该对象:
int i=0;
int *p=&i;
cout<<*p;//由符号*得到指针p指向的对象i的值
空指针不指向任何对象,有几种生成空指针的方法
int *p=nullptr;
int *p=0;
int *p=NULL;
任何非0指针对应的条件值都是true。
void* 指针可以存放任何对象的地址,但不能通过它直接操作void*指针指向的对象。
通过的个数可以区分指针的级别,如**为指针的指针。
引用本身不是一个对象所以不存在指向引用的指针,但指针本身是一个对象,所以存在对指针的引用。
int i=42;
int *p;
int *&r=p;
r=&i;
*r=0;
2.4 const限定符
用关键字const对变量的类型加以限定,这样任何对变量的赋值行为都将引发错误。
1. const的引用
把引用绑定到常量上称为常量的引用。
const int ci=1024;
const int &cii=ci;//引用和其对应的变量都是常量
我们允许为一个常量的引用绑定一个非常量的对象、字面值,甚至是一个一般表达式。但不允许通过常量引用修改引用对象(包括非常量)的值。
2. const和指针
指向常量的指针不能用于改变所指对象的值。要想存放指向常量对象的地址只能使用指向常量的指针。
const int ci=1024;
const int *cp=&ci;
同样地,也允许指向常量的指针指向一个非常量的对象,但不能通过指向常量的指针来改变其所指对象的值。
const指针
指针是对象,因此允许把指针定义为一个常量:常量指针。常量指针必须初始化,而且一旦初始化就不能改变它的值。把*放在const之前来说明指针是一个常量。
int i=1024;
int *const ic=&i;
能不能通过常量指针来改变所指对象的值取决于所指对象是不是常量。
为了区分指针本身是常量和所指对象是常量这两个完全独立的概念,用名词**顶层const(top-level const)来表示指针本身是常量,而底层const(low-level const)**表示指针所指对象是常量。
3. constexpr常量表达式
const expression,常量表达式,指值不会改变且在编译过程中就能得到计算结果的表达式。字面值为常量表达式,用常量表达式(字面值初始化)的const对象也是常量表达式。
const int sz=get_size();//sz不是常量表达式
constexpr变量
声明为constexpr的变量一定是一个常量且必须用常量表达式初始化。
声明cosntexpr时用到的类型为字面值类型,算术类型、引用和指针都属于字面值类型,自定义类、IO库、string类型不属于字面值类型。
注意:constexpr声明中如果定义一个指针,constexpr只对指针有效。比较以下二者的区别
const int *p=nullptr; //指向整型常量--的指针
constexpr int *q=nullptr;//指向整型的--常量指针
2.5 处理类型
1. 类型别名(type alias)
某种类型的同义词,有两种方法来定义类型别名:
- 用关键字 typedef:
typedef double wages; //wages是double的同义词
type wages base,*p;//base是double的同义词,p是double*的同义词。
- 用别名声明来定义类型的别名
using SI=Sales_item;
这里存在一个容易犯错的陷阱:
typedef char *pstring;
const pstring cstr=0;
cosnt pstring *ps;
问:cstr和ps分别是什么类型的指针?
答案是cstr是指向char类型的常量指针,而ps是一个指向指向char的常量指针的指针。
我们常常容易错误的替换类型别名为:
const char *cstr=0;//错误的理解
注意pstring 是指向char的指针,所以const pstring 就是指向char的常量指针,
2. auto类型说明符
auto 让编译器通过初始值来推算变量的类型。auto定义的变量必须要有初始值。用auto在一条语句中声明多个变量时所有变量的类型必须一致。auto通常会忽略掉顶层const而保留底层const。
const int ci=0;
auto b=ci;//b是一个整数
auto e=&ci;//e是一个指向整数常量的指针
3. decltype
decltype的作用是选择并返回操作数的数据类型。
const int ci=0,&cj=ci,*p=&ci;
decltype(ci) x=0;
decltype(cj) y=x;
decltype(cj+0) z;
decltype(*p) c=ci;//int&
decltype((ci)) d=ci;//int&
需要注意的是decltype(*p)的类型是int&,而不是int。
decltype((variable))的结果必定是引用。
2.6 自定义数据类型
我们可以用两个不同的关键字来定义一个类:struct和class,两个关键字的区别仅仅是默认的访问权限不同,类的第一个访问符之前的成员一个是public的一个是private的。
如初步定义一个Sales_data的类:
struct Sales_data{
std::string bookNo;
unsigned units_sold=0;
double revenue=0.0;
}
编写头文件
预处理器(preprocessor):确保头文件多次包含仍能安全工作的常用技术。是从c语言继承来的,预处理器是在编译之前执行的一段程序。可以部分地改变我们所写的程序。如预处理功能#include,预处理器看到#include标记时就会用指定的头文件的内容代替#include。还需要用到的以下预处理功能是头文件保护符(header guard),头文件保护符依赖于预处理变量,预处理变量有两种状态:已定义和未定义。#define将一个名字设定为预处理变量,另外两个指令分别检查某个指定的预处理变量是否已经定义:#ifdef当且仅当变量已定义为正,#ifndef当且仅变量未定义为真。一旦检查结果为真,则执行后续操作直到遇到**#endif**指令为止。
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data{
std::string bookNO;
unsigned units_sold=0;
double revenue=0.0;
};
#endif
最后附上一个简单示范,按照目前学到的这些关于编写头文件的说明,来编写代码解决初步的书店问题,首先是头文件
#ifndef S_H
#define S_H
#include<string>
struct sales {
std::string bookid;
unsigned sold = 0;//销售册数
double revenue = 0.0;//销售单价
double getsum() { return sold * revenue; }//总销售额
};
#endif
头文件定义的类非常简单,三个成员变量一个成员函数,也不涉及其他复杂的操作,这样就行了,复杂的操作先暂时放到主程序里
#include<iostream>
#include"s.h"
#include<string>
using namespace std;
int main(){
sales test;
if (cin >> test.bookid>>test.sold>>test.revenue) {
sales test1;
while (cin >> test1.bookid >> test1.sold >> test1.revenue) {
if (test1.bookid == test.bookid) {
test.revenue = (test.getsum() + test1.getsum()) / (test.sold+test1.sold);
test.sold+=test1.sold;
}
else {
cout << "bookid: " << test.bookid << "销售册数: " << test.sold << "单价为 " << test.revenue << endl;
test.bookid = test1.bookid;
test.revenue = test1.revenue;
test.sold = test1.sold;
}
}
cout << "bookid: " << test.bookid << "销售册数: " << test.sold << "单价为 " << test.revenue << endl;
}
return 0;
}
上面主程序实现了输入销售记录,书的id号相同的销售纪录会合并,输入不同id号时会马上输出之前的销售纪录,等后面学到更多关于c++类的特性时我们再来完善这个书店问题的程序。