一、基础知识
1.4控制流
1.在for循环中,循环控制变量的初始化和修改都放在语句头部分,形式较简洁,且特别适用于循环次数已知的情况。在while循环中,循环控制变量的初始化一般放在while语句之前,循环控制变量的修改一般放在循环体中,形式上不如for语句简洁,但它比较适用于循环次数不易预知的情况(用某一条件控制循环)。两种形式各有优点,但它们在功能上是等价的,可以相互转换。
2.读取数量不定的输入数据
#include<iostream>
using namespace std;
int main()
{
int sum = 0, value = 0;
while (cin >> value)//读取数据直到遇到文件尾,计算所有读入的值之和
sum += value;
cout << "Sum is " << sum << endl;
return 0;
}
windows系统中,文件结束符的方法是ctrl+z,然后按Enter或Return。
1.5类简介
练习1.20
头文件Sales_item.h
#ifndef SALESITEM_H
#define SALESITEM_H
#include <iostream>
#include <string>
class Sales_item
{
public:
Sales_item(const std::string& book) :isbn(book), units_sold(0), revenue(0.0) {}
Sales_item(std::istream& is) { is >> *this; }
friend std::istream& operator>>(std::istream&, Sales_item&);
friend std::ostream& operator<<(std::ostream&, const Sales_item&);
public:
Sales_item& operator+=(const Sales_item&);
public:
double avg_price() const;
bool same_isbn(const Sales_item& rhs)const
{
return isbn == rhs.isbn;
}
Sales_item() :units_sold(0), revenue(0.0) {}
public:
std::string isbn;
unsigned units_sold;
double revenue;
};
using std::istream;
using std::ostream;
Sales_item operator+(const Sales_item&, const Sales_item&);
inline bool operator==(const Sales_item& lhs, const Sales_item& rhs)
{
return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.same_isbn(rhs);
}
inline bool operator!=(const Sales_item& lhs, const Sales_item& rhs)
{
return !(lhs == rhs);
}
inline Sales_item& Sales_item::operator +=(const Sales_item& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs);
ret += rhs;
return ret;
}
inline istream& operator>>(istream& in, Sales_item& s)
{
double price;
in >> s.isbn >> s.units_sold >> price;
if (in)
s.revenue = s.units_sold * price;
else
s = Sales_item();
return in;
}
inline ostream& operator<<(ostream& out, const Sales_item& s)
{
out << s.isbn << "\t" << s.units_sold << "\t" << s.revenue << "\t" << s.avg_price();
return out;
}
inline double Sales_item::avg_price() const
{
if (units_sold)
return revenue / units_sold;
else
return 0;
}
#endif
主函数
#include <iostream>
#include "Sales_item.h"
int main(int argc, char** argv)
{
Sales_item item;
while (std::cin >> item)
std::cout << item << std::endl;
getchar();
return 0;
}
1.6书店程序
读取销售记录,生成每本书销售报告,显示售出册数、总销售额、平均售价。
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item total;
if (std::cin >> total)
{
Sales_item trans;
while (std::cin >> trans)
{
if (total.isbn == trans.isbn)
total += trans;
else
{
std::cout << total << std::endl;
total = trans;
}
}
std::cout << total << std::endl;
}
else
{
std::cerr << "No date!" << std::endl;
return -1;
}
return 0;
}
二、变量和基本类型
2.1基本内置类型
1.选择类型
1.long一般与int有一样的尺寸,
2.算数表达式不用char或bool,存放字符或布尔值时才使用,因为char的有无符号与机器有关系;
3.一般用double,因为与float比精度高,但计算代价小,long double 运行消耗不容忽视。
2.类型转换
1.对无符号的类型一个超出范围的值,则为该值对无符号数取模的余数
2.注意无符号类型做循环时,负数的取余行为导致的死循环。
3.字面值常量、转义序列
1.指定字面值类型,前后缀的使用表2.2
前缀:u unicode16字符
U unicode32字符
L 宽字符
u8 utf-8
后缀:u或U 表示该字面值为无符号类型
l或L 表示该字面值的类型至少为long
ll或LL 表示该字面值的类型至少为long long
f或F 表示该字面值为float类型
前后缀可以交叉结合使用:后缀UL时,表示无符号长整型。
字符用单引号‘’,字符串用双引号“”。
十进制:20 八进制:020 十六进制:0x20或者0X20
2.2变量
1.初始化与赋值的不同之处
2.声明与定义的区别:
声明一个变量而非定义它 ,在变量名前添加关键字extern;
2.3复合类型
一条声明语句由一个基本数据类型和一个声明符号列表组成。
2.3.1引用(别名)
定义:int i = 2; int &g = i;
必须初始化,且是绑定对象的别名,不能引用引用,不能与字面值或表达式的计算结构绑定;
2.3.2指针
double dval;
double *pd=&daval;//指向double类型对象的地址
1.*解引用符,得出所指的对象
2.空指针 int*p=nullptr;或0;或NULL;
3.任何非0指针指向的条件值都是true;
4.void*指针:存放一个地址,对地址到底是什么类型的变量不了解,之后继续会讲到。
2.3.3符合类型的声明
类型修饰符只是声明的一部分:
int i=1024,*p=&i,&r=i;
1.可以有指向指针的指针,可以多次解引用,如**ppi和***pppi;
2.可以有指针的引用,反过来不行,如 int *&r=p;
2.4const限定符
1const对象必须初始化,对象只在文件内有效,多文件内可以独立定义。
2.需要在多文件中共享时,用extern关键字
extern const int bufSize;
2.4.1const的引用
const int ci=1024;
const int &r1=ci;
1.作为“常量的引用”,不能修改所绑定的对象,
2.初始化常量引用时允许用任意表达式作为初始值,只要表达式能转换成引用的类型即可。
int i=42;
const int &r1=i;
const int &r2=r1*2;//r2是一个常量引用
3.可以绑定到另外一个类型上:
double dval=3.14;
const int &ri =dval;
4.const引用可能引用一个非常量、字面值、一般表达式,可以通过其他途径改变值,但是普通的引用不能绑定到const引用上;
int i=42;
int &r1=i;
const int &r2=i;//可以引用非常量
5.不能用非常量引用来引用const常量;
const int g=10;
int %r3=g;
2.4.2指针和const
1.指向常量的指针:用法与const引用差不多;
//指向常量的指针可以指向一个非常量对象
double dval =3.14;
const double *cptr=&dval; //指向常量的指针可以指向一个非常量对象
2.常量指针
1.必须初始化,且不再改变,即存储的地址不在变,但是指向对象的内容可以根据对象的类型进行修改。
int errNumb = 0;
int* const curErr = &errNumb;
const double pi=3.14159;
const double *const pip=π//pip是一个指向常量对象的常量指针
通过从右向左阅读,搞清楚声明的含义
2.4.3顶层const
1.顶层const可以表示任意的对象是常量,底层const则与指针和引用等复合类型的基本类型有关。
2.指针可以使顶层也可以是底层const,
const int * const p3=p2;//第一个是底层const,第二个是顶层const;
3.拷贝对象时,顶层const不受影响,底层const
考虑时,可以不在乎顶层const,但是必须清楚指向的对象是常量。p58页此处比较饶,需要分清楚多看几次。
2.4.4constexpr和常量表达式
1.不会改变并且在编译过程就能得到计算结果的表达式,const对象、字面值都是。
2.将变量声明为constexpr类型,一定是常量,而且必须用常量表达式初始化;
3.字面值类型;
4.constexpr声明如果定义一个指针,则对指针有效,所指对象无关
constexpr int *p=nullptr;//用contexpr将q置为顶层const
2.5处理类型
2.5.1类型别名
1.传统方法:typedef double wages
新标准规定:using SI=Sales_item;
2.定义复合类型或常量时多注意:typedef char *pstring;是将char*取别名pstring。
2.5.2auto类型说明符
编译器根据初始值来推算变量的类型。
auto item = val1+val2;//0.必须有初始值,且一条语句中所有变量的初始基本数据类型都必须一样。
1.编译器将引用对象的类型(与decltype不同)作为auto的类型
2.auto一般会忽略顶层const,保留底层const
3.如果需要推断出的auto类型是顶层const,则需要明确指出
const auto f=ci;ci为int类型, f为const int类型
4.将引用类型设为auto,则原来的初始化规则仍然适用。
5.切记,符号*和&只从属于某个声明,而非基本数据类型的一部分,因此初始值必须是同一类型:
auto k = ci, &l = i; // k是整数,l是整型引用
auto &m = ci, *p = &ci; // m是对整型常量的引用,p是指向整型常量的指针
auto &n = i, *p2 = &ci; // Error: i的类型是int,而&ci的类型是const int
2.5.3decltype类型指示符
选择并返回操作数的数据类型
decltype(fun()) sum = x; //将fun()的返回值类型作为sum的类型
1.如果decltype适用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)
注意:返回的类型为引用类型时,用于定义变量需要初始化!
2.如果返回的表达式不是一个变量,则decltype返回表达式结果对应的类型
int i=42,*p=&i,&r=i;
decltype (r+0) b;//满足2为int
decltype(*p) c;//满足1,解引用操作*p,则返回类型为int&,此处错误
3.decltype的表达式多加了一层括号的变量,结果将是永远是引用!!!。
decltype ((i)) d;//d是int &,必须初始化,此处错误
decltype (i) d;//int
赋值的表达式语句本身是一种引用
decltype(a=b)d=a;
decltype与auto主要的区别有两点:
1:如果使用引用类型,auto会识别为其所指对象的类型,decltype则会识别为引用的类型。
2:decltype(())双括号的差别。