2.1 基本内置类型
1.算术类型分为两类:整型(integral type,包括字符和布尔类型在内)和浮点型。
布尔类型(bool)的取值是真(true)或者假(false)。
2.除去布尔型和扩展的字符型之外,其他整型可以划分为带符号的(signed)和无符号的(unsigned)两种。带符号类型可以表示正数、负数或0,无符号类型则仅能表示大于等于0的值。
类型int、short、long和long long都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号类型,例如unsigned long。类型unsigned int可以缩写为unsigned。
与其他整型不同,字符型被分为了三种:char、signed char和unsigned char。特别需要注意的是:类型char和类型signed char并不一样。尽管字符型有三种,但是字符的表现形式却只有两种:带符号的和无符号的。类型char实际上会表现为上述两种形式中的一种,具体是哪种由编译器决定。
3.unsigned也能接收负值,不过会自动将这个负值转化为正值。
4.以0开头的数代表八进制,以0x开头的数表示十六进制
5.由单引号括起来的一个字符称为char型字面值,双引号括起来的零个或多个字符则构成字符串型字面值。
6.转义字符
- 换行符 \n 横向制表符 \t 报警(响铃)符 \a
- 纵向制表符 \v 退格浮 \b 双引号 \"
- 反斜线 \\ 问号 \? 单引号 \'
- 回车符 \r 进纸符 \f
7.指定字面值的类型
2.2 变量
1.变量定义的基本形式是:首先是类型说明符(type specifier),随后紧跟由一个或多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。列表中每个变量名的类型都由类型说明符指定,定义时还可以为一个或多个变量赋初值。
2.对象是指一块能存储数据并具有某种类型的内存空间。
3.初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。
4.作为C++11新标准的一部分,用花括号来初始化变量得到了全面应用,而在此之前,这种初始化的形式仅在某些受限的场合下才能使用。这种初始化的形式被称为列表初始化(list initialization)。现在,无论是初始化对象还是某些时候为对象赋新值,都可以使用这样一组由花括号括起来的初始值了。
5.
6.变量命名规范
变量命名有许多约定俗成的规范,下面的这些规范能有效提高程序的可读性:
标识符要能体现实际含义。
变量名一般用小写字母,如index,不要使用Index或INDEX。
用户自定义的类名一般以大写字母开头,如Sales_item。
如果标识符由多个单词组成,则单词间应有明显区分,如student_loan或studentLoan,不要使用studentloan。
对于命名规范来说,若能坚持,必将有效。
2.3 复合类型
1.引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。
2.指针值
指针的值(即地址)应属下列4种状态之一:
1.指向一个对象。
2.指向紧邻对象所占空间的下一个位置。
3.空指针,意味着指针没有指向任何对象。
4.无效指针,也就是上述情况之外的其他值。
3.当用到一个预处理变量时,预处理器会自动地将它替换为实际值,因此用NULL初始化指针和用0初始化指针是一样的。在新标准下,现在的C++程序最好使用nullptr,同时尽量避免使用NULL。
4.指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中最重要的一点就是引用本身并非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。
5.void*是一种特殊的指针类型,可用于存放任意对象的地址。一个void*指针存放着一个地址,这一点和其他指针类似。
6.一般来说,声明符中修饰符的个数并没有限制。当有多个修饰符连写在一起时,按照其逻辑关系详加解释即可。以指针为例,指针是内存中的对象,像其他对象一样也有自己的地址,因此允许把指针的地址再存放到另一个指针当中。
通过*的个数可以区分指针的级别。也就是说,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推。
7.引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。
2.4 const限定符
1.const对象一旦创建后其值就不能再改变,所以const对象必须初始化。一如既往,初始值可以是任意复杂的表达式。
2.如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
3.指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const(top-level const)表示指针本身是个常量,而用名词底层const(low-level const)表示指针所指的对象是一个常量。
4.顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。比较特殊的是,指针类型既可以是顶层const也可以是底层const,这一点和其他类型相比区别明显。
- int i = 0;
- int *const p1 = &i; // 不能改变p1的值,这是一个顶层const
- const int ci = 42; // 不能改变ci的值,这是一个顶层const
- const int *p2 = &ci; // 允许改变p2的值,这是一个底层const
- const int *const p3 = p2; // 靠右的const是顶层const,靠左的是底层const
- const int &r = ci; // 用于声明引用的const都是底层const
5.常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。
6.c++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。
7.常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为"字面值类型"(literal type)。
2.5 处理类型
1.类型别名(type alias)是一个名字,它是某种类型的同义词。使用类型别名有很多好处,它让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。
有两种方法可用于定义类型别名。传统的方法是使用关键字typedef:
- typedef double wages; //wages是double的同义词
- typedef wages base, *p; //base是double的同义词,p是double*的同义词
- using SI = Sales_item; // SI是Sales_item的同义词
2.decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
3.decltype((variable))(注意是双层括号)的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用。
2.6 自定义数据结构
1.我们的类以关键字struct开始,紧跟着类名和类体(其中类体部分可以为空)。类体由花括号包围形成了一个新的作用域)。类内部定义的名字必须唯一,但是可以与类外部定义的名字重复。
2.确保头文件多次包含仍能安全工作的常用技术是预处理器(preprocessor),它由C++语言从C语言继承而来。预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序。之前已经用到了一项预处理功能#include,当预处理器看到#include标记时就会用指定的头文件的内容代替#include。
3.C++程序还会用到的一项预处理功能是头文件保护符(header guard),头文件保护符依赖于预处理变量(参见2.3.2节,第53页)。预处理变量有两种状态:已定义和未定义。#define指令把一个名字设定为预处理变量,另外两个指令则分别检查某个指定的预处理变量是否已经定义:#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif指令为止。
PS:部分练习答案
练习2.1
在C++中,short与int至少16位,long最少32位,long long最少64位
无符号类型只能表示非负数,带符号类型能表示正数,负数和零
一般而言float是32位,double是64位
练习2.2
应该选择浮点型(double或者float)
因为它们都可能包含有小数部分
练习2.6
int month=9,day=7
这里都是十进制
month = 09
这是不合法的,因为0开头代表八进制,八进制是不会出现9的
day = 07
这个day是八进制数
练习2.8
- #include<iostream>
- int main()
- {
- std::cout<<2<<"\115\012";
- std::cout<<2<<"\t\115\012";
- return 0;
- }
练习2.15
a,c合法
b不合法,初始化必须使用一个对象
d不合法,必须初始化
练习2.16
都合法
a:改变d的值为3.14159
b:自动转换类型
c,d:值会变小
练习2.18
- #include<iostream>
- int main()
- {
- int a = 0,b = 1;
- int *p1 = &a,*p2 = p1;
- std::cout<<p1<<" "<<*p2<<std::endl;
- p1 = &b; //更改指针的值
- *p2 = b; //更改指针所指对象的值
- std::cout<<p1<<" "<<*p2<<std::endl;
- }
练习2.19
指针是指向一个其他类型
引用是一个对象的别名
区别:
1. 指针是一个实体,而引用仅是个别名;
2. 引用使用时无需解引用(*),指针需要解引用;
3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
4. 引用没有 const,指针有 const;
5. 引用不能为空,指针可以为空;
6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
7. 指针和引用的自增(++)运算意义不一样;
8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
练习2.20
p1指向i,i的值改变成1764
练习2.21
a,b不合法
a:不能使用int*来初始化double*
b:不能使用int来初始化int*
练习2.22
if(p) 指针是否是nullptr
if(*p) 指针所指的位置的值是否为0
练习2.30
v2 顶层
p2 底层
p3 靠近p3的const是顶层,最左边的是底层
r2 底层
练习2.41
- //1.5.1
- #include<iostream>
- #include<string>
- struct Sale_data{
- std::string bookNo;
- unsigned units_sold;
- double revenue;
- };
- int main()
- {
- Sale_data book;
- double price;
- std::cin>>book.bookNo>>book.units_sold>>price;
- book.revenue = book.units_sold*price;
- std::cout<<book.bookNo<<" "<<book.units_sold<<" "<<book.revenue<<std::endl;
- }
- //1.5.2
- #include <iostream>
- #include <string>
- struct Sale_data
- {
- std::string bookNo;
- unsigned units_sold = 0;
- double revenue = 0.0;
- };
- int main()
- {
- Sale_data book1, book2;
- double price1, price2;
- std::cin >> book1.bookNo >> book1.units_sold >> price1;
- std::cin >> book2.bookNo >> book2.units_sold >> price2;
- book1.revenue = book1.units_sold * price1;
- book2.revenue = book2.units_sold * price2;
- if (book1.bookNo == book2.bookNo)
- {
- unsigned totalCnt = book1.units_sold + book2.units_sold;
- double totalRevenue = book1.revenue + book2.revenue;
- std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
- if (totalCnt != 0)
- std::cout << totalRevenue / totalCnt << std::endl;
- else
- std::cout << "(no sales)" << std::endl;
- return 0;
- }
- else
- {
- std::cerr << "Data must refer to same ISBN" << std::endl;
- return -1;
- }
- }
- //1.6
- #include <iostream>
- #include <string>
- struct Sale_data
- {
- std::string bookNo;
- unsigned units_sold = 0;
- double revenue = 0.0;
- };
- int main()
- {
- Sale_data total;
- double totalPrice;
- if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
- {
- total.revenue = total.units_sold * totalPrice;
- Sale_data trans;
- double transPrice;
- while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
- {
- trans.revenue = trans.units_sold * transPrice;
- if (total.bookNo == trans.bookNo)
- {
- total.units_sold += trans.units_sold;
- total.revenue += trans.revenue;
- }
- else
- {
- std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
- if (total.units_sold != 0)
- std::cout << total.revenue / total.units_sold << std::endl;
- else
- std::cout << "(no sales)" << std::endl;
- total.bookNo = trans.bookNo;
- total.units_sold = trans.units_sold;
- total.revenue = trans.revenue;
- }
- }
- std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
- if (total.units_sold != 0)
- std::cout << total.revenue / total.units_sold << std::endl;
- else
- std::cout << "(no sales)" << std::endl;
- return 0;
- }
- else
- {
- std::cerr << "No data?!" << std::endl;
- return -1;
- }
- }
练习2.42
- //Sales_data.h
- #include <string>
- #include <iostream>
- struct Sales_data {
- std::string bookNo;
- unsigned units_sold = 0;
- double revenue = 0.0;
- void CalcRevenue(double price);
- double CalcAveragePrice();
- void SetData(Sales_data data);
- void AddData(Sales_data data);
- void Print();
- };
- void Sales_data::CalcRevenue(double price)
- {
- revenue = units_sold * price;
- }
- void Sales_data::SetData(Sales_data data)
- {
- bookNo = data.bookNo;
- units_sold = data.units_sold;
- revenue = data.revenue;
- }
- void Sales_data::AddData(Sales_data data)
- {
- if (bookNo != data.bookNo) return;
- units_sold += data.units_sold;
- revenue += data.revenue;
- }
- double Sales_data::CalcAveragePrice()
- {
- if (units_sold != 0)
- return revenue / units_sold;
- else
- return 0.0;
- }
- void Sales_data::Print()
- {
- std::cout << bookNo << " " << units_sold << " " << revenue << " ";
- double averagePrice = CalcAveragePrice();
- if (averagePrice != 0.0)
- std::cout << averagePrice << std::endl;
- else
- std::cout << "(no sales)" << std::endl;
- }
- <pre name="code" class="cpp">//1.5.1
- #include <iostream>
- #include "Sales_data.h"
- int main()
- {
- Sales_data book;
- double price;
- std::cin >> book.bookNo >> book.units_sold >> price;
- book.CalcRevenue(price);
- book.Print();
- return 0;
- }
- //1.5.2
- #include <iostream>
- #include "Sales_data.h"
- int main()
- {
- Sales_data book1, book2;
- double price1, price2;
- std::cin >> book1.bookNo >> book1.units_sold >> price1;
- std::cin >> book2.bookNo >> book2.units_sold >> price2;
- book1.CalcRevenue(price1);
- book2.CalcRevenue(price2);
- if (book1.bookNo == book2.bookNo)
- {
- book1.AddData(book2);
- book1.Print();
- return 0;
- }
- else
- {
- std::cerr << "Data must refer to same ISBN" << std::endl;
- return -1;
- }
- }
- //1.5.2
- #include <iostream>
- #include "Sales_data.h"
- int main()
- {
- Sales_data book1, book2;
- double price1, price2;
- std::cin >> book1.bookNo >> book1.units_sold >> price1;
- std::cin >> book2.bookNo >> book2.units_sold >> price2;
- book1.CalcRevenue(price1);
- book2.CalcRevenue(price2);
- if (book1.bookNo == book2.bookNo)
- {
- book1.AddData(book2);
- book1.Print();
- return 0;
- }
- else
- {
- std::cerr << "Data must refer to same ISBN" << std::endl;
- return -1;
- }
- }
- //1.6
- #include <iostream>
- #include "Sales_data.h"
- int main()
- {
- Sales_data total;
- double totalPrice;
- if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
- {
- total.CalcRevenue(totalPrice);
- Sales_data trans;
- double transPrice;
- while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
- {
- trans.CalcRevenue(transPrice);
- if (total.bookNo == trans.bookNo)
- {
- total.AddData(trans);
- }
- else
- {
- total.Print();
- total.SetData(trans);
- }
- }
- total.Print();
- return 0;
- }
- else
- {
- std::cerr << "No data?!" << std::endl;
- return -1;
- }
- }