C++PRIMER 学习笔记

2016/3/3
cerr 对象又叫作标准错误,通常用来输出警告和错误信息给程序的使用者。而 clog 对象用于产生程序执行的一般信息。
告诉编译器要使用 iostream 库。尖括号里的名字是一个。 头文件。程序使用库工具时必须包含相关的头文件。#include 指示必须单独写成一行——头文件名和 #include 必须在同一行。通常,#include 指示应出现在任何函数的外部。而且习惯上,程序的所有 #include 指示都在文件开头部分出现。
 
  编写程序,要求用户输入一组数。输出信息说明其中有多少个负数。解决
                                
标准库的头文件用尖括号 < > 括起来,非标准库的头文件用双引号 " " 括起来。
成员函数只定义一次,但被视为每个对象的成员。我们将这些操作称为成员函数,是因为它们(通常)在特定对象上操作。在这个意义上,它们是对象的成员,即使同一类型的所有对象共享同一个定义也是如此。
当调用成员函数时,(通常)指定函数要操作的对象。语法是使用点操作符(.):item1.same_isbn意思是“命名为 item1 的对象的 same_isbn 成员”。点操作符通过它的左操作数取得右操作数。点操作符仅应用于类类型的对象:左操作数必须是类类型的对象,右操作数必须指定该类型的成员。

通常使用成员函数作为点操作符的右操作数来调用成员函数。执行成员函数和执行其他函数相似:要调用函数,可将调用操作符(())放在函数名之后。调用操作符是一对圆括号,括住传递给函数的实参列表(可能为空)。

字符类型有两种:char 和 wchar_t。char 类型保证了有足够的空间,能够存储机器基本字符集中任何字符相应的数值,因此,char 类型通常是单个机器字节(byte)。wchar_t 类型用于扩展字符集,比如汉字和日语,这些字符集中的一些字符不能用单个 char 表示。short、 int 和 long 类型都表示整型值, 存储空间的大小不同。一般, short类型为半个机器字长,int 类型为一个机器字长,而 long 类型为一个或两个机 器字长(在 32 位机器中 int 类型和 long 类型通常字长是相同的)。

2016/3/4
有些字符是不可打印的。不可打印字符实际上是不可显示的字符,比如退格或者控制符。还有一些在语言中有特殊意义的字符,例如单引号、双引号和反斜线符号。不可打印字符和特殊字符都用转义字符书写。转义字符都以反斜线符号开始,C++ 语言中定义了如下转义字符:
换行符 \n 水平制表符 \t纵向制表符 \v 退格符 \b回车符 \r 进纸符 \f报警(响铃)符 \a 反斜线 \\疑问号 \? 单引号 \'双引号 \"

正如第 1.4.1 节提到的,C++ 的格式非常自由。特别是有一些地方不能插入空格,其中之一是在单词中间。特别是不能在单词中间断开一行。但可以通过使用反斜线符号巧妙实现:// ok: A \ before a newline ignores the line breakstd::cou\t << "Hi" << st\d::endl;等价于std::cout << "Hi" << std::endl;

1. 左值(发音为 ell-value):左值可以出现在赋值语句的左边或右边。2. 右值(发音为 are-value):右值只能出现在赋值的右边,不能出现在赋值语句的左边。
变量是左值,因此可以出现在赋值语句的左边。数字字面值是右值,因此不能被赋值。

初始化变量定义指定了变量的类型和标识符,也可以为对象提供初始值。定义时指定了初始值的对象被称为是已初始化的。C++ 支持两种初始化变量的形式: 复制初始化和直接初始化。复制初始化语法用等号(=),直接初始化则是把初始化式放在括号中
int ival(1024); // direct-initializationint
ival = 1024; // copy-initialization
这两种情形中,ival 都被初始化为 1024。虽然在本书到目前为止还没有清楚说明,但是在 C++ 中理解“初始化不是赋值”是必要的。初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替使用 = 来初始化变量使得许多 C++ 编程新手感到迷惑,他们很容易把初始化当成是赋值的一种形式。但是在 C++ 中初始化和赋值是两种不同的操作。这个概念特别容易误导人, 因为在许多其他的语言中这两者的差别不过是枝节问题因而可以被忽略。即使在 C++ 中也只有在编写非常复杂的类时才会凸显这两者之间的区别。无论如何,这是一个关键的概念,也是我们将会在整本书中反复强调的概念。
直接初始化语法更灵活且效率更高。

变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用 extern 关键字声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字 extern:extern int i; // declares but does not define iint i; // declares and defines iextern 声明不是定义,也不分配存储空间。事实上,它只是说明变量定义在程序的其他地方。程序中变量中变量可以声明很多次,但只能定义一次。
任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。


与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定 const 变更为 extern,就可以在整个程序中访问 const 对象:
非 const 变量默认为 extern。要使 const 变量能够在其他的
文件中访问,必须地指定它为 extern。

引用是一种复合类型,通过在变量名前添加“&”符号来定义。
复合类型是 指用其他类型定义的类型。在引用的情况下,每一种引用类型都“关联到”某一 其他类型。不能定义引用类型的引用,但可以定义任何其他类型的引用。 引用必须用与该引用同类型的对象初始化: 这一规则的结果是必须在定义引用时进行初始化。初始化是指明引用指向哪个对象的唯一方法。
非 const 引用只能绑定到与该引用同类型的对象。及 int &rval1 = 1.01;是非法的; const int &rval3= 1;是合法的

typedef 可以用来定义类型的同义词: typedef double wages; // wages is a synonym for double
typedef int exam_score; // exam_score is a synonym for int
typedef wages salary; // indirect synonym for double typedef
名字可以用作类型说明符: wages hourly, weekly; // double hourly, weekly;
exam_score test_result; // int test_result; typedef
定义以关键字 typedef 开始,后面是数据类型和标识符。标识符 或类型名并没有引入新的类型,而只是现有数据类型的同义词。typedef 名字可 出现在程序中类型名可出现的任何位置。 typedef 通常被用于以下三种目的:
为了隐藏特定类型的实现,强调使用类型的目的。
简化复杂的类型定义,使其更易理解。
允许一种类型用于多个目的,同时使得每次使用该类型的目的明确。


每个类都定义了一个接口和一个实现。接口由使用该类的代码需要执行的操作组成。实现一般包括该类所需要的数据。实现还包括定义该类需要的但又不供一般性使用的函数。定义类时,通常先定义该类的接口,即该类所提供的操作。通过这些操作,可以决定该类完成其功能所需要的数据, 以及是否需要定义一些函数来支持该类的实现。


如果使用 class 关键字来定义类,那么定义在第一个访问标号前的任何成员都隐式指定为 private;如果使用 struct 关键字,那么这些成员都是public。使用 class 还是 struct 关键字来定义类,仅仅影响默认的初始访问级别。


2016/3/5
头文件为相关声明提供了一个集中存放的位置。头文件一般包含类的定义、extern 变量的声明和函数的声明。函数的声明将在第 7.4 节介绍。使用或定义这些实体的文件要包含适当的头文件
头文件的正确使用能够带来两个好处:保证所有文件使用给定实体的同一声明;当声明需要修改时,只有头文件需要更新。
对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类、值在编译时就已知道的 const 对象和 inline 函数(第 7.6 节介绍 inline 函数)。这些实体可在多个源文件中定义,只要每个源文件中的定义是相同的.
因此,设计头文件时,应使其可以多次包含在同一源文件中,这一点很重要。我们必须保证多次包含同一头文件不会引起该头文件定义的类和对象被多次定义。使得头文件安全的通用做法,是使用预处理器定义头文件保护符。头文件保护符用于避免在已经见到头文件的情况下重新处理该头文件的内容

预处理器变量有两种状态:已定义或未定义。定义预处理器变量和检测其状态所用的预处理器指示不同。#define 指示接受一个名字并定义该名字为预处理器变量。#ifndef 指示检测指定的预处理器变量是否未定义。如果预处理器变量未定义,那么跟在其后的所有指示都被处理,直到出现 #endif。

可以使用这些设施来预防多次包含同一头文件:
#ifndef SALESITEM_H
#define SALESITEM_H// Definition of Sales_itemclass and related functions goes here
#endif
条件指示
1#ifndef SALESITEM_H
测试 SALESITEM_H 预处理器变量是否未定义。如果 SALESITEM_H 未定义,那么 #ifndef 测试成功,跟在 #ifndef 后面的所有行都被执行,直到发现#endif。相反,如果 SALESITEM_H 已定义,那么 #ifndef 指示测试为假,该指示和 #endif 指示间的代码都被忽略。

#include 指示接受以下两种形式:#include <standard_header>#include "my_file.h"如果头文件名括在尖括号(< >)里,那么认为该头文件是标准头文件。编译器将会在预定义的位置集查找该头文件, 这些预定义的位置可以通过设置查找路径环境变量或者通过命令行选项来修改。使用的查找方法因编译器的不同而差别迥异。建议你咨询同事或者查阅编译器用户指南来获得更多的信息。如果头文件名括在一对引号里,那么认为它是非系统头文件,非系统头文件的查找通常开 始于源文件所在的路径。

使用 using 声明可以在不需要加前缀 namespace_name:: 的情况下访问命名空间中的名字。using 声明的形式如下:using namespace::name;一旦使用了 using 声明,我们就可以直接引用名字,而不需要再引用该名字的命名空间。

使用using指示不使用using声明,不使用using:namespace std。

关系操作符 <,<=,>,>= 分别用于测试一个 string 对象是否小于、小于或等于、大于、大于或等于另一个 string 对象:
关系操作符比较两个 string 对象时采用了和(大小写敏感的)字典排序相同的策略:
如果两个 string 对象长度不同, 且短的 string 对象与长的 string 对象的前面部分相匹配,则短的 string 对象小于长的 string 对象。
如果 string 对象的字符不同,则比较第一个不匹配的字符。
string substr = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
则 substr 小于 phrase,而 slang 则大于 substr 或 phrase

当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作数必须至少有一个是 string 类型的:

vector 不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector 类型的每一种都指定了其保存元素的类型。因此,vector<int> 和 vector<string> 都是数据类型。

2016/3/6
vector的下标操作不添加元素。
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements

每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。如
果容器中有元素的话,由 begin 返回的迭代器指向第一个元素:
vector<int>::iterator iter = ivec.begin();
上述语句把 iter 初始化为由名为 vector 操作返回的值。假设 vector 不
空,初始化后,iter 即指该元素为 ivec[0]。
由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”。“超出
末端迭代器”(off-the-end iterator)。表明它指向了一个不存在的元素。
如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。

由 end 操作返回的迭代器并不指向 vector 中任何实际的元
素,相反,它只是起一个哨兵(sentinel)的作用,表示我们
已处理完 vector 中所有元素。

由于 end 操作返回的迭代器不指向任何元素,因此不能对它进
行解引用或自增操作

const_iterator
前面的程序用 vector::iterator 改变 vector 中的元素值。每种容器类型
还定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素, 但不能改变其值
当我们对普通 iterator 类型解引用时,得到对某个元素的非 const
(2.5 节)。而如果我们对 const_iterator 类型解引用时,则可以得到一个指 向 const 对象的引用(2.4 节),如同任何常量一样,该对象不能进行重写。
例如,如果 text 是 vector<string> 类型,程序员想要遍历它,输出每个
元素,可以这样编写程序:
// use const_iterator because we won't change the elements
for (vector<string>::const_iterator iter = text.begin();
iter != text.end(); ++iter)
cout << *iter << endl; // print each element in text
除了是从迭代器读取元素值而不是对它进行赋值之外,这个循环与前一个相 似。由于这里只需要借助迭代器进行读,不需要写,这里把 iter 定义 为 const_iterator 类型。当对 const_iterator 类型解引用时,返回的是一 个 const 值。 不允许用 const_iterator: 进行赋值
for (vector<string>::const_iterator iter = text.begin();
iter != text.end(); ++ iter)
*iter = " "; // error: *iter is const
使用 const_iterator 类型时,我们可以得到一个迭代器,它自身的值可以 改变,但不能用来改变其所指向的元素的值。可以对迭代器进行自增以及使用解 引用操作符来读取值,但不能对该元素赋值。
不要把 const_iterator 对象与 const 的 iterator 对象混淆起来。声明
一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的 值:
vector<int> nums(10); // nums is nonconst
const vector<int>::iterator cit = nums.begin();
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
const_iterator 对象可以用于 const vector 或非 const vector,因为不能 改写元素值。const 迭代器这种类型几乎没什么用处:一旦它被初始化后,只 能用它来改写其指向的元素,但不能使它指向任何其他元素。

2016/3/10
迭代器只有 iter-或+ n(一个整数值),以及迭代器之间相减,得出之间的距离。(没有相加)

表 3.6 列出了 bitset 的构造函数。类似于 vector,bitset 类是一种类模板; 而与 vector 不一样的是 bitset 类型对象的区别仅在其长度而不在其类型。在定义 bitset 时,要明确 bitset 含有多少位,须在尖括号内给出它的长度值:

当用 string 对象初始化 bitset 对象时,string 对象直接表示为位模式。从 string 对象读入位集的顺序是从右向左(from right to left):
string strval("1100");
bitset<32> bitvec4(strval);
bitvec4 的位模式中第 2 和 3 的位置为 1,其余位置都为 0。如果 string 对象的字符个数小于 bitset 类型的长度,则高阶位置为 0。string 对象和 bitsets 对象之间是反向转化的:string 对象的最右边字符 (即下标最大的那个字符)用来初始化 bitset 对象的低阶位(即下标为 0 的位)。当用 string 对象初始化 bitset 对象时,记住这一差别很重要

2016/3/11
bitset还有很多问题不会

2016/3/12
字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始 化,也可以用一个字符串字面值进行初始化。然而,要注意这两种初始化形式并 不完全相同,字符串字面值(第 2.2 节)包含一个额外的空字符(null)用于 结束字符串。当使用字符串字面值来初始化创建的新数组时,将在新数组中加入 空字符:
char ca1[] = {'C', '+', '+'}; // no null
char ca2[] = {'C', '+', '+', '\0'}; // explicit null
char ca3[] = "C++"; // null terminator added automatically

定义二维数组
以前我要建立一个二维数组,总是使用
int N=5, M=6;
vector<vector<int> > Matrix(N);
for(int i =0; i< Matrix.size(); i++){
   Matrix[i].resize(M);
}
上面多写了三行,就是为了指定 Matrix 的烈数。
今天才发现,可以用更简单的方法完成上面的工作
下面的一行就可以代替上面的四行
vector<vector<int> > Matrix(N, vector<int>(M));

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值