45
46 常量指针必须初始化
47 一条语句可以定义出不同类型的变量 int i=10, *p=&i,&r =i;
48 应该是int p 而不是int p
49 **表示指向指针的指针 p52
50 指针是对象,所以存在对于指针的引用
int *p;
int *&r=p;
51 在默认状态下 ,const对象只是在文件内部有效
52 只在一个文件中定义const 而在其他多个文件中声明并使用它:
唯一的解决办法是 对于const变量不管是声明还是定义都添加 extern 关键字,这样只需要定义一次就够了
file_1.cc定义并初始化一个常量: extern const int bufsize = fcn();
file_1.h头文件:extern const int buffSize ;//与file_1.cc中定义的buffSize是同一个
53 const 的引用 (对常量的引用) 不能修改他所绑定的对象
const int ci=1024;
const int &r1=ci;
54 常量可以引用常量和非常量,非常量只能引用非常量
55 初始化常量引用时允许用任意的表达式作为初始值 p55
56 double dval =3.14;
const int &ri=dval;
这里编译器创建了一个临时量对象
const int temp=dval; //由双精度浮点数生成一个临时的整型常量
const int &ri=temp; //让ri绑定这个临时常量
而非常量无法跨类引用!!
57 int i=42;
const int &r2 =i; //r2绑定对象i,但是不允许通过r2修改i的值
(常量引用非常量)
58 允许令一个指向常量的指针指向非常量
double dval =3.14;
const double *cptr =&dval;(指向常量的指针不能通过cptr改变对象的值)
59 const(指向常量对象) double *const(常量指针) pip =& pi(z指向常量对象的常量指针) p56
60 顶层const:指针本身是个常量 (不允许改变指针pi的值(地址))
底层const:指针所指向的对象是个常量 (允许改变指针pi的值(地址))
声明引用&的const都是底层const (所引用的对象是个常量)
const int ci =42; 顶层const (不允许改变ci的值)
61 考入和考出的对象必须拥有相同的底层const资格,或者两个对象的数据类型必须能够转换,一般来说,非常量可以转换为常量
62 常量拷贝给非常量是可以的,但是不能非常量引用常量
63 指向常量的指针可以指向指向非常量的指针,但是反过来不行
64
65 允许将变量声明为constexpr 类型以便由编译器来验证变量的值是否是一个常量表达式,声明为constexpr的值一定是一个常量,而且必须由常量表达式初始化 (C++11特性)
66 字面值类型: 算数类型,引用,指针
67 定义于函数体内的变量没有固定地址,不能使用constexpr ;而函数体外部的对象定义时的地址固定不变,可以使用constexpr
68
如果constexpr声明中如果定义了一个指针,那么constexpr只对指针有效,对于其所指向的对象无关
constexpr int * q =nullptr 相等于 int const * q 表示q是一个指向整数的常量指针(顶层const)
constexpr const int *p= &i; 顶层 底层
69 类型别名 typedef /using 变量别名是&
typedef double wages; //wages 是double的一个别名
typedef wages base , *p; //base是 double的另一个别名 p是double *的别名
using SI= int; //SI是int 的别名
这里注意 typedef char * pstring ;
const pstring cstr=0; cstr是指向char的常量指针
上面这句话不等于 const char * cstr =0; p61
这种理解是错误的 ,用到了pstring 其基本数据类型是指针, 而用char *重写过后,数据类型就变成了char ,*成为了声明符的一部分
70 让编译器分析表达式类型 auto (C++11 新特性)
使用auto 时一条语句只能有一个基本数据类型
auto i = 0 , *p=&i; //i为整数,p为整型指针
71 使用引用其实是在使用引用的对象,当引用参与初始化时,真正参与初始化的是引用对象的值
72 auto一般会忽略顶层const 而底层const 会被保留下来
73 auto 对常量对象取地址是一种底层const
74 不能为非常量引用绑定字面值,可以为常量引用绑定字面值
75 设置一个类型为 auto的引用时,初始值中的顶层常量属性任然保留。和往常一样,如果我们给初始值绑定一个引用,则此时的常量就不是顶层常量了。
76
77 使用decltype返回类型(c++11特性) 如果decltype使用的表达式是一个变量,那么decltype返回该变量的类型(包括顶层const以及引用在内)
p63
decltype(cj) x=0;
78 decltype®的结果是引用类型,如果想让结果是r所指的类型,那么可以把r作为表达式的一部分,如r+0,显然这个表达式的结果是一个具体值而不是引用
如果是解引用操作,那么decltype将得到引用类型 因此,decltype(*p)的结果类型是int &而不是int
79 decltype((variable))双层括号的结果永远是引用,而 devcltype(variable)的结果只有当变量本身就是一个引用的时候才是引用
80 类体后面可以紧跟变量名以示对该类型对象的定义,所以分号不能少
81 对象的定义 和类的定义 最好不要放在一起
82
83 2.41 习题复习
#include <iostream>
#include <string>using namespace std;
class Sales_data {
friend std:: istream & operator >> (std::istream& , Sales_data&);
friend std:: ostream& operator <<(std::ostream&, const Sales_data&);
friend bool operator <(const Sales_data&, const Sales_data&);
friend bool operator==(const Sales_data&, const Sales_data&);
public:Sales_data() = default;Sales_data(const std::string& book) :bookNo(book) {}Sales_data(std::istream& is) { is >> *this; }
public:Sales_data& operator +=(const Sales_data&);std::string isbn() const { return bookNo; }
private:std::string bookNo;unsigned units_sold = 0;double sellingprice = 0;double saleprice = 0.0;double discount = 0.0;
};
inline bool compareIsbn(const Sales_data& lhs, const Sales_data& rhs)
{return lhs.isbn() == rhs.isbn();
}Sales_data operator +(const Sales_data&, const Sales_data&);inline bool operator == (const Sales_data& lhs, const Sales_data& rhs)
{return lhs.units_sold == rhs.units_sold && lhs.sellingprice == rhs.sellingprice && lhs.saleprice == rhs.saleprice && lhs.isbn() == rhs.isbn();
}inline bool operator !=(const Sales_data& lhs, const Sales_data& rhs)
{return !(lhs == rhs);
}Sales_data& Sales_data::operator+=(const Sales_data& rhs)
{units_sold += rhs.units_sold;saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold) / (rhs.units_sold + units_sold);if (sellingprice != 0){discount = saleprice / sellingprice;return *this;}
}Sales_data operator +(const Sales_data& lhs, const Sales_data& rhs)
{Sales_data ret(lhs);ret += rhs;return ret;
}std::istream& operator>>(std::istream& in, Sales_data& s)
{in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;if (in && s.sellingprice != 0){s.discount = s.saleprice / s.sellingprice;}else{s = Sales_data();}return in;
}
std:: ostream& operator <<(std::ostream& out, const Sales_data& s)
{out << s.isbn() << " " << s.units_sold << " " << s.sellingprice << " " << s.saleprice << " " << s.discount;return out;
}int main()
{Sales_data book;cout << "请输入销售记录" << endl;while (cin >> book) {cout << "ISBN,售出本书,原始价格,实售价格,折扣为" << book << endl;}Sales_data trans1, trans2;cout << "请输入两条ISBN相同的销售记录" << endl;cin >> trans1 >> trans2;if (compareIsbn(trans1, trans2)){cout << "汇总信息:ISBN,售出本书,原始价格,实售价格,折扣为" << trans1 + trans2 << endl;}else{cout << "两条销售记录的ISBN不同" << endl;}Sales_data total, trans;cout << "请输入几条ISBN相同的销售记录" << endl;if (cin >> total) {while (cin >> trans){if (compareIsbn(total, trans))total = total + trans;elsecout << "当前书籍ISBN不同" << endl;break;}cout << "有效汇总信息:ISBN,售出本数,原始价格,实售价格,折扣为" << total << endl;}else{std::cout << "没有数据" << endl;return -1;}int num = 1;cout << "请输入若干销售记录:" << endl;if (cin >> trans1) {while (cin >> trans2){if (compareIsbn(trans1, trans2))num++;else{cout << trans1.isbn() << "共有" << num << "条销售记录" << endl;trans1 = trans2;num = 1;}cout << trans1.isbn() << "共有" << num << "条销售记录" << endl;}}else{cout << "没有数据" << endl;return -1;}return 0;
}
84 确保头文件多次包含还是能安全工作的常用技术是预处理器
预处理功能(头文件保护符)
#ifdef 当且仅当变量已定义为真
#ifndef 当且仅当变量未定义为真,一旦检查结果为真,则执行后续操作指导遇到#endif指令为止
85
如果后面再包含sales_data.h 那么#ifndef的检查结果为假,那么编译器将会忽略#ifndef到#endif 之间的部分
86 基于头文件中类的名字构建保护符的名字,以确保其唯一性,一般把预处理变量的名字全部大写
87 头文件即使还没有包含在任何其他头文件中,也应该设置保护符
88 const 对象一旦定义就无法再赋新值,所以必须初始化
89 #ifndef #define #endif
90 两种重要的标准库类型:string vector
91 用using 声明不需要后面专门使用前缀
using namespace::name;
每一个名字都需要单独的声明
92 头文件不应该包含using 声明,如果这样,每个使用该头文件的文件都会有这个声明,会产生一些不必要的名字冲突
93 六种初始化string 的方式
94 拷贝初始化和直接初始化:没有等号的是直接初始化
95 string的操作 p77
96 cin>>s //将string对象读入s,遇到空白停止
97 string 连续输入输出
98 使用getline函数读一行数,直到遇到换行符为止,
99 size_type是size函数返回的类型
s.size()<n如果n是负数,那么n会转化为一个比较大的无符号值
100 如果一条表达式中已经有了size()函数就不要使用int 了,这样可以避免混用int 和 unsigned 可能带来的问题
101 字符串之间比大小,先是比长度再是比第一对相异字符
102 当字符字面值和字符串字面值相加时,必须保证加号两边的运算对象其中一个是string类型(字符串字面值不是string对象)
103 string的输入运算符自动忽略开头的空白,从第一个真正的字符开始读起,知道遇到下一处空白为止
getline从输入流中读取数据,知道遇到换行符位置,换行符也被读进去,但是不会储存在最后的字符串中
104 cctype头文件 #include 函数和定义 p82
105 基于范围的for语句
想要改变对象中的字符值,必须把循环变量定义成引用类型
106 使用下标时,将下标的类型设为string::size_type 这个是个无符号数,所以不会小于0,只要让其小于size()就可以了
107 vector 是一个类模板 而 非类型
vector<vector > a c++11特性 后面要空一格
其中每个元素都是vector的对象
108 vector 初始化 对象 p88
109 push_back函数 p90
运用push_back给vector对象添加新元素
110 vector对象的高效增长 先定义一个空的vector对象,再在运行时向其中添加具体值(动态添加元素)
111 vector操作 p91
112 vector ::size_type 正确(必须指定它是由哪种类型定义的)
113 不是所有的vector对象都能互相比较
114 vector对象不能直接通过下表添加元素,必须使用push_back,下标运算只能用于访问已经存在的元素,而不能用来添加元素
115 遍历vector对象不同元素的每个字符
116 练习3.20
117 string 和 vector都支持迭代器(除了下标运算可以访问string对象的字符或vector对象的元素,也可以使用迭代器)
118 begin是负责返回指向第一个元素的迭代器
end是负责范围指向容器尾部元素的下一个位置(尾后迭代器)
如果容器为空begin和end同时返回同一个迭代器,都是尾后迭代器
119 如果两个迭代器指向的元素都是同一个容器的尾后迭代器,那么他们相等;否则不等
120 标准容器迭代器的运算符 p96
121 可以通过解引用迭代器来获得它所指示的元素
122 const_iterator iterator 是可以表示迭代器的类型,两者区别是后者可读可写,前者不可写(每个容器类定义了一个名为iterator的类型,该类型支持迭代器概念所规定的一套操作)
123 cbegin cend 是c++11新特性为了得到const_iterator类型的返回值(无论vector是否是常量)
124 (*it).empty() 解引用调用迭代器所指向类的成员函数 it->empty()也行
125 不能在范围for循环中向vector对象添加元素,改变vector容量的操作会使得vector对象的迭代器失效
凡是使用迭代器的循环体,都不要像迭代器所属的容器添加元素!
126 迭代器运算 p99-100
difference_type是带符号整型数,是两个迭代器距离的返回类型
127 二分搜索法 p100