《C++ Primer Plus 第六版》学习记录 CH1-CH4
CH1 预备知识
面向对象编程 OOP:Object Oriented Programming——类
泛型编程:genetic programming——模板
CH2 开始学习C++
#include <iostream>
//输入输出流文件
//io指的是输入和输出,使用cin和cout必须包含该头文件
using namespace std;
//using编译指令,此时使用的是iostream,而不是iostream.h
//c++编译器的标准组件:类,函数,变量都是,被放置在名称空间std中
//using使得std名称空间的所有名称可用,而不用写:
std::cout<<""<<std::endl; //cout<<""<<endl;
std::cin<<""; //cin<"";
//最好还是采用名称空间代码 std::
类描述了一种数据类型的全部属性(包括可执行操作),对象是根据这些描述创建的实体。
函数用于创建C++程序的模块。需要区分函数原型和函数,例如:
double sqrt(double); //是一个原型,原型只描述函数接口
double sqrt(double) //编译器将把这个解释为函数头
CH3 处理数据
3.1 简单变量
C++字节通常包含8位,它确保的是最小长度:
- short至少16位;
- int至少与short一样长;
- long至少32位,且至少与int一样长;
- long long至少64位,且至少与long一样长。
sizeof 运算符返回类型或变量的字节长度,查询1byte多少bit可用:
#include <climits>
cout << CHAR_BIT << endl; //字节的位数
变量初始化方式:
int a;
a=10; //先声明再赋值
int a=10; //将赋值和声明合并在一起
//C++11可将大括号初始化器用于任何类型,单值,数组,结构之类的
int a{10};
int a={10}; //均为a=10
int a={}; //此时赋值为0
无符号数:
//unsigned 本身是 unsigned int缩写
unsigned short a;
unsigned int b;
unsigned b; //默认int
为了可移植性以及节省内存,选择合适的整型类型很重要!!
常量:C++通过后缀确定常量类型,一般存储为int
char类型: 处理字符(字母和数字),字符用单引号,字符串用双引号。
char ch;
cin >> ch; //键入M
cout << ch << end; //输出M,ch中储存的是77,输入时cin把M转为77,输出时cout吧77转换为M
char ch = 'M';
int i = ch;
cout << ch << endl; //输出为字符 M
cout << i << endl; //输出为ASCII码 77
cout.put(ch); //该函数显示一个字符,输出 M
cout.put(); //通过类对象cout来使用函数put()
通用字符名:unicode,ISO 10646之类的,先放放;
wchar_t:宽字符类型;
char16_t,char_32_t:放。
bool类型:0为false,非零true。
3.2 const限定词
常量名称一般将首字母大写(更好区分),或者以字母k开头。
创建常量的通用格式:
const type name = value;
const int Months = 12; //应在声明时就对const进行初始化
3.3 浮点数
三种浮点类型: cout一般默认输出小数点后6位
- float:通常32位;
- double:通常64位;
- long double:通常80、96或128位。
3.4 C++算数运算符
浮点数计算:
float a = 50.25;
float b = 11.17;
//cout默认保留6位有效数字,因为后面为0,所以省略得到61.42
std::cout << a + b << std::endl; //输出61.42
//第一句是控制输出精度,保留小数点后6位,这个结构是因为浮点数的固有问题
//因为转换成二进制计算,转换过程中以及计算过程中都会有误差,所以会得到小数
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::cout << a + b << std::endl; //输出61.419998
类型转换:
- 浮点数转换为整型采取截断,而非四舍五入;
- 不同类型进行算术运算时,会将较小的类型转换为较大的类型;
- 强制转换时,不会修改变量本身,而是创建新的指定类型的变量;
课后题:
C++提供了什么措施来防止超出整型的范围?
C++没有提供自动防止超出整型限制的功能,可以使用头文件climits来确定限制情况。
CH4 复合类型
4.1 数组
声明包括:
- 元素类型;
- 数组名;
- 元素数。
typeName arrayName[arraySize]; //arraySize必须是整型常量或const值,或常量表达式
int a[3]; //索引从0开始
int a[3] = {0,1,2}; //声明时也可以初始化数组a[0]=0,a[1]=1,a[2]=2
int a[3] = {0}; //若只初始化了部分,其他自动设置为0,a[0]=0,a[1]=0,a[2]=0
在C++11中,初始化有了些优化:
//初始化时可以省略等号
int a[3] {0,1,2};
//可以大括号里不包含东西,将自动都填充为0
int a[3] = {};
int a[3] {};
//初始化禁止缩窄转换
int a[3] = {1,1.22222}; //not allowed
4.2 字符串——c-风格字符串
以空字符null character结尾,空字符被写作\0,ASCII码为0,用来标记字符串结尾:
char a[3] = {'a','b','c'}; //不是字符串,会输出abc以及内存中随后的东西,知道遇到空字符
char a[3] = {'a','b','\0'}; //是字符串,只会输出ab,因为遇到空字符停止
由于上述过于繁琐,采用字符串常量或字符串字面值的方法:
char a[3] = "ab"; //双引号的字符串隐式的包含了空字符,键盘键入时也会自动加上空字符
char a[5] = "ab"; //后面的会被填充为空字符,需要注意数组长度不要设置太短了
#include <cstring> //字符串相关函数
char a[15] = "ab";
cout << strlen(a)<< endl; //输出2,strlen()返回字符串本身可见字符的长度,不含空字符
cout << sizeof(a)<< endl; //输出15,sizeof()返回整个数组字节
cin只包含一个单词输入的字符串时,不会有问题,但是当输入a b时,cin第一次只会识别到a,因为cin使用空白(空格、制表符和换行符)确定字符串结束位置,因此此时需要采用另一种读取字符串的方法,采用面向行,而不是面向单词:
//面向行的输入 getline(),get()——使用回车键入换行符来确定输出结尾
cin.getline(name,20); //将姓名读取到name数组中,getline()丢弃换行符
cin.get(name,ArSize);
cin.get(dessert,ArSize); //get()保留换行符到输入队列中,因此无法读取甜品
//修改方式是可以在两句中间添加cin.get(),或者如下方式处理换行符
cin.get(name,ArSize).get();
cin.get(dessert,ArSize).get();
混合输入(数字和字符串)也需要注意这个问题:
int year;
cin >> year; //cin读取year后,换行符留在了输入队列
char address[80];
cin.getline(address,80); //cin.getline看到换行符,会认为是空行,无法输入地址
cout << year << endl;
cout << address << endl;
//解决方案同上
cin >> year;
cin.get();
新的思考在于:既然cin会把换行符留在输入队列,那下一次cin的时候为什么能成功读取,而不是空行?
查询后结果:使用cin读入字符时,默认是跳过中间的空格以及可能的制表符和换行符。
4.3 string类简介
#include <string> //需要包含头文件
string str1; //string对象是声明为简单变量,而不是数组
string str2 = "aaa";
cin >> str1; //程序读取输入时,自动调整str长度
string类的相关操作:
//赋值
string str1;
string str2 = "abcd";
str1 = str2; //这在数组中不被允许
//拼接
string str3;
str3 = str1 + str2; //得到abcdabcd
str1 += str2; //同样得到abcdabcd
//字符串长度
str1.size() //str1是类对象,len()是方法
string类I/O
char charr[20];
string str;
cont << strlen(charr) << end; //输出不确定,对于未初始化的数组,空字符是随机的
cont << str.size() << end; //输出为0
cin.getline(charr,20); //此时getline是类方法
getline(cin,str); //此时getline不是类方法
对于字符串字面值,先省略?
4.4 结构
//结构声明,定义结构
struct product0 //product0为新类型的名称
{
char name[20]; //结构中成员
double price;
}
//创建这种类型变量
product0 hat; //hat是一种结构
product0 cloths; //cloths是一种结构
hat.price; //访问成员,hat变量的price成员
//C++11结构初始化
product0 hat {"aaa",2.33};
product0 hat = {"aaa",2.33}; //等号可选
product0 hat = {}; //若大括号未包含东西,各成员将被设置为0
//可以对同类型结构赋值
cloths = hat; //此时cloths中各成员与hat相同
//结构数组:结构里就可以包含数组,也可创建元素为结构的数组
product0 gifts[100]; //gifts数组,其实每个元素都是product0
4.5 共用体union
共用体是一种数据格式,能储存不同的数据类型,但只能同时存储其中一种类型,常用于节省内存。
常用于操作系统数据结构或硬件数据结构。
暂放。
4.6 枚举
enum工具提供了另一种创建符号常量的方式。
enum spectrum {red,green,yellow}; //spectrum为新类型的名字,被称为枚举
//red,green,yellow作为符号常量,对应整数值0,1,2,被称为枚举量
//枚举只定义了赋值运算符,但是只能将定义时候的枚举量赋给这种枚举的变量
spectrum blue; //声明blue变量
blue = red; //赋值后,blue为0
blue = 100; //错误
//可以显式的设置枚举量的值,可以全部设置,也可部分,可以创建值相同的枚举量
enum bits {a1, zero = 0, two = 2, four = 4, a2}; //此时a1默认为0,a2为5
4.7 指针和自由存储空间
计算机存储数据时必须跟踪的3种基本属性:
- 信息存储在何处;
- 存储的值为多少;
- 存储的信息是什么类型。
面向对象编程和过程性编程区别:OOP强调的是在运行阶段(而不是编译阶段)进行决策。
//声明指针,两边空格可选
int * ptr; //ptr是指向int类型的指针
//初始化指针
int a = 5;
int * p = &a;
以前学过的是将指针初始化为变量的地址,此时的指针只是为了可以通过直接访问的内存(编译时候)提供了一个别名。指针真正的用武之处在于,在运行阶段分配未命名的内存以储存值。
//new分配的内存块通常与常规变量声明的不同,常规变量存储在栈区,而new从堆或自由存储区分配内存,使程序在管理内存方面有更大控制权。
int * pn = new int; //先分配内存
*pn = 1001; //再在给内存中存储值
//各种过程后,需要释放内存,new和delete一定配对使用,不然会泄露内存
delete pn; //释放内存
在编译时给数组分配内存被称为静态联编(static binding),但使用new时,如果运行阶段需要数组,则创建,不需要就不创建,这称为动态联编(dynamic binding),意味着数组是在程序运行是创建的。这种数组较动态数组(dynamic array)。前者编写时指定数组长度,后者运行时确定数组长度。
//使用new创建动态数组
int * psome = new int [3]; //psome是指向数组第一个元素的指针
//使用动态数组,只要把指针当做数组名使用即可
psome[0] = 0.1;
psome[1] = 0.2;
psome[2] = 0.3;
cout << psome[1] << endl; //输出0.2
psome = psome + 1; //此时指针指向数组第二个元素
cout << psome[0] << endl; //输出0.2
psome = psome - 1; //将指针指回原来的地址,以便删除
delete [] psome; //[]告诉程序,应释放整个数组,而不仅仅是指针指向元素
4.8 指针、数组和指针算数
C++将数组名解释为地址。指针变量+1后,增加的量等于它指向的类型的字节数。
int * psome = new int [3];
psome[0] = 0.1;
psome[1] = 0.2; //psome[1]和*(psome+1)是等价的
psome[2] = 0.3;
cout << psome << endl; //第一个元素的地址,等效于&psome[0]
cout << &psome << endl; //整个数组的地址
数组和指针的关系可以拓展到C风格字符串:
//给cout提供一个字符的地址,则它将从该字符开始打印,直到遇到空字符
//用引号括起的字符也像数组名一样,是第一个元素的地址
char f[10] = "rose"; //数组名是第一个元素的地址
cout << f << " aaa" << endl; //此时输出rose aaa
//一般给cout提供指针它将打印地址,但如果是char *,则将显示指向的字符串。此时要显示地址,必须强制转换为另一种指针类型
char * ps;
ps = f;
//f赋给ps并不是复制字符串,而是复制地址,两个指针指向相同的内存单元和字符串
cout << f <<endl; //显示rose
cout << (int *) f <<endl; //显示地址
cout << ps <<endl; //显示rose
cout << (int *) ps <<endl; //显示地址
//获得字符串副本。应使用strcpy()或strmcpy(),而不是赋值运算符将字符串赋给数组
//strmcpy()比strcpy()多一个参数,可以设置复制的最大字符数
pss = new char[strlen(animal)+1]; //获得新的内存空间
strcpy (pss,f); //将f指向的字符串复制到pss中
//啊,使用string类可以不用那么麻烦
动态数组优于静态,对于结构也是如此:
struct product0 //product0为新类型的名称
{
char name[20]; //结构中成员
double price;
}
int main()
{
product0 * ps = new product0; //为该结构分配内存
cin.get(ps->name,20);
cin >> ps->price; //结构标识符是指针。使用箭头运算符
// cin >> (*ps).price; //结构标识符是结构名,使用句点运算符
return 0;
}
根据用于分配内存的方法,C++有4种管理数据内存的方式:——CH9
- 自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量。局部变量,作用于包含它的代码块。通常存储在栈中,后进先出(LIFO);
- 静态存储:整个程序执行期间都存在的储存方式。一种是在函数外面定义它,另一种是声明变量时使用关键字static;
- 动态存储 :new和delete管理了一个内存池,在C++C中成为自由储存空间或堆;
- 线程存储:C++11新增的,CH9见。
4.9 类型组合
这个还好,就是数据、结构和指针的组合:
struct product0 //product0为新类型的名称
{
char name[20]; //结构中成员
double price;
}
product0 s1,s2,s3; //结构变量
s1.price = 10;
product0 * pa = &s2; //指向这种结构的指针
pa->price = 12;
product0 s_stru[3]; //结构数组
s_stru[0].price = 22;
const product0 *arp[3] = {&s1,&s2,&s3} //指针数组
cout << arp[1]->year <<endl; //输出12
const product0 ** ppa = arp; //指向上述数组的指针
cout << (*ppa)->year <<endl; //输出10
auto ppb = ppa; //C++11提供auto可自动确定类型
cout << (*(ppb+1))->year <<endl; //输出12
至于为什么指针数组要在前面前const,先插个眼o.0
4.10 数组的替代品
模板类 vector,是一种动态数组,可以在运行阶段设置vector对象的长度。vector类确实使用new和delete来管理内存,但这种工作是自动完成的。使用示例:
#include <vector> //功能比数组强大,但效率低
using namespace std;
vector<int> vi; //std::vector
int n;
cin >> n;
vector<double> vd(n); //如果要指定长度,括号里可以直整型常量或变量
模板类 array (C++11),也位于名称空间std中,长度是固定的。
#include <array> //效率与数组相同,但更方便
//必须提前指定长度,且为整型常量
using namespace std;
array<int,5> ai; //std::array
array<double,4> ad = {1.2,2.1,3.43,4.3};
0 一些自述
有点基础但不多,平时主要在用matlab,希望这段时间能一直坚持看完这本!!!以上内容并不全,更多的是针对自身的一些小记录。