C++ Primer学习笔记(一)

时间:2020-12-14 08:39:05

始终对C++念念不忘,看过 一个32岁入门的70后程序员给我的启示  之后,心情激荡,更是一发不可收拾。

认真地说,我不是一个执着的人,见异思迁,好读书而不求甚解,兼之情绪化(~~ 某些方面),于是怒下决心要掌握C++,于是有了这个笔记。

比较欣慰的是,可能认真学了Java,又看过不少的资料,所以对编程这回事多少有了自己的理解,所以现在学起来如有神助。可见编程语言都是相通的。

1、C++ 并没有直接定义进行输入或输出(IO)的任何语句,这种功能是由标准库提供的。如iostream标准库。
    像 istream 和 ostream 这样的库类型,都是定义为类的,也就是说,它们严格说来不是语言的一部分
 
2、当我们使用 istream 对象作为条件,结果是测试流的状态。如果流是有效的(也就是说,如果读入下一个输入是可能的)那么测试成功。遇到文件结束符或遇到无效输入时,如读取了一个不是整数的值,则 istream 对象是无效的。
处于无效状态的 istream 对象将导致条件失败。      while(std::cin>>val)
 
3、标准库的头文件用尖括号 < > 括起来,非标准库的头文件用双引号 " " 括起来。
 
4、形参:parameter list;实参:argument。
 
5、buffer(缓冲区),一段用来存放数据的存储区域。IO 设备常存储输入(或输出)到缓冲区,并独立于程序动作对缓冲区进行读写。输出缓冲区通常必须显式刷新以强制输出缓冲区内容。默认情况下,读 cin 会刷新 cout;当程序正常结束时,cout 也被刷新。 
 
6、基本内置类型。
    算数类型的存储空间依机器而定。C++标准规定了每个算术类型的最小存储空间,但它并不阻止编译器使用更大的存储空间。
    表示整数、字符和布尔值的算术类型合称为整型
    short、int 和 long 类型都表示整型值,存储空间的大小不同。一般,short 类型为半个机器字长,int 类型为一个机器字长,而 long 类型为一个或两个机器字长(在 32 位机器中 int 类型和 long 类型通常字长是相同的)。
 
    在位这一级上,存储器是没有结构和意义的。
    让存储具有结构和意义的是块(chunk)存储器。一般块的位数是2的幂。
    通常将 8 位的块作为一个字节,32 位或 4 个字节作为一个“字(word)”。
    大多数计算机将存储器中的每一个字节和一个称为地址的数关联起来。可以说地址xxx的字节。
    要让地址为 xxx 的字节具有意义,必须要知道存储在该地址的值的类型。
 
    bool 类型表示真值 true 和 false。可以将算术类型的任何值赋给 bool 对象。0 值算术类型代表 false,任何非 0 的值都代表 true。 
 
    字面值常量:42、3.14159、"a"、'a'、true等。
    为什么是字面值?因为只能用其值来称呼它。
    为什么是常量?因为它不能修改。
    注意:只有内置类型有字面值常量。
    
    以 0(零)开头的字面值整数常量表示八进制,以 0x 或 0X 开头的表示十六进制。
    字面值整数常量的类型默认为 int 或 long 类型。以最小最适合的类型做为其类型。也可以指定如3L表示long类型的字面值。类似地,可通过在数值后面加 U 或 u 定义 unsigned 类型。
    默认的浮点字面值常量为 double 类型。在数值的后面加上 F 或 f 表示单精度。同样加上 L 或者 l 表示扩展精度(再次提醒,不提倡使用小写字母 l)。
    单词 true 和 false 是布尔型的字面值。
    在字符字面值前加 L 就能够得到 wchar_t 类型的宽字符字面值。如L'a'。
 
    字符串字面值,不是基本内置类型,而是一串常量字符。同样也有 L"string"。   std::string。
    为了兼容 C 语言,C++ 中所有的字符串字面值都由编译器自动在末尾添加一个空字符宽字符串字面值是一串常量宽字符,同样以一个宽空字符结束。
    字符串字面值的连接:两个相邻的仅由空格、制表符或换行符分开的字符串字面值(或宽字符串字面值),可连接成一个新字符串字面值。如下:
std::cout<<"Line 1, "
<<"line 2, "
<<"line 3, "
<<"end"
<<std::endl;
    
    在一行的末尾加一反斜线符号可将此行和下一行当作同一行处理。如:
std::cou\
t<<std\
::endl;
    注意:\ 之后一直到下一行的字母之前,只能有换行符!!不能有空格等内容。
 
7、变量提供了程序可以操作的有名字的存储区。
 
    直接初始化、复制初始化。直接初始化语法更灵活且效率更高。对内置类型来说,复制初始化和直接初始化几乎没有差别。 
    int ival(1024); // direct-initialization 
    int ival = 1024; // copy-initialization 
    注意:赋值和初始化是不同的概念,赋值是擦除对象的值,并用新值代替。
 
    从概念上讲,赋值操作确实需要做一些工作。它必须先把 st1 占用的相关内存释放掉,然后再分配给 st2 足够存放 st2 副本的内存空间,最后把 st2 中的所有字符复制到新分配的内存空间。
 
8、类类型中,定义如何进行初始化的成员函数称为构造函数。
 
9、内置类型变量的初始化:内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始化成 0,在函数体里定义的内置类型变量不进行自动初始化。
     类类型的初始化:如果有默认构造函数,那定义的时候就会自动调用默认构造函数来初始化,否则不初始化。例如std::string,默认是"",即空字符串。
 
10、使用 extern 关键字声明变量名而不定义它。定义也是声明。声明用于向程序表明变量的类型和名字,它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。 
        extern int i;
        int i;
        也可以 extern int i = 10; 但没什么必要。
    注意:在使用变量之前,必须声明或定义。
    变量从声明开始才可见!!!
 
11、const限定符,将变量转成常量, 从而不可修改。因为常量在定义后就不能被修改,所以定义时必须初始化。
    但是,与其他变量不同,const常量的作用域仅限于其所在的文件,哪怕该文件被引入也不行。除非使用external const定义。
    由此推知,非 const 变量默认为 extern。
 
12、引用就是对象的另一个名字(别名,类似Java的对象引用)。在实际程序中,引用主要用作函数的形式参数。
    引用是一种复合类型,通过在变量名前添加“&”符号来定义。
    因为引用只是它绑定的对象的另一名字,作用在引用上的所有操作事实上都是作用在该引用绑定的对象上
 
    str2=str1;类似于Java中的基本类型赋值,而非引用!!!如果想引用,使用string &str2=str1;
 
    const变量的引用,必须也是const。如: const int &refVal = ival;
    
13、typedef type1 type2;  可以给类型取别名。
 
14、枚举,一组有关联的数据。
    如:enum open_modes {input, output, append} 
    默认,从0开始给枚举成员赋值。也可以指定值,另外,还可以重复值。
 
15、c++的类,以class开头,后面跟着{};,注意,有分号
    类也可以包含 0 个到多个 private 或 public 访问标号。访问标号控制类的成员在类外部是否可访问。使用该类的代码可能只能访问 public 成员。
 
    struct关键字,也可以定义类类型,继承自C。
    默认情况下,struct 的成员为 public,而 class 的成员为 private。(高级的c++默认私有)
 
16、关键词:分别编译、链接、头文件。
    头文件用于声明,而非定义。
 
    对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类、值在编译时就已知道的 const 对象和 inline 函数(第 7.6 节介绍 inline 函数)。这些实体可在多个源文件中定义,只要每个源文件中的定义是相同的。 
 
    const 变量默认时是定义该变量的文件的局部变量。正如我们现在所看到的,这样设置默认情况的原因在于允许 const 变量定义在头文件中。 WHY????
    如果 const 变量不是用常量表达式初始化,那么它就不应该在头文件中定义。相反,和其他的变量一样,该 const 变量应该在一个源文件中定义并初始化。应在头文件中为它添加 extern 声明,以使其能被多个文件共享。
 
 
17、#include 设施是 C++ 预处理器的一部分。预处理器处理程序的源代码,在编译器之前运行。
    预处理器用指定的头文件的内容替代每个 #include。
 
    设计头文件时,应使其可以多次包含在同一源文件中,这一点很重要。例如std::string可能被多次间接引入。
    使得头文件安全的通用做法,是使用预处理器定义头文件保护符header guard。
 
    预处理器变量有两种状态:已定义或未定义。定义预处理器变量和检测其状态所用的预处理器指示不同。#define 指示接受一个名字并定义该名字为预处理器变量。#ifndef 指示检测指定的预处理器变量是否未定义。如果预处理器变量未定义,那么跟在其后的所有指示都被处理,直到出现 #endif。
    可以使用这些设施来预防多次包含同一头文件: 
#ifndef SALESITEM_H
#define SALESITEM_H
// Definition of Sales_itemclass and related functions goes here
#endif
18、using声明。
        using std::cout;    //单个
        using namespace std;    //全部
 
19、string 类型支持长度可变的字符串。
    因为历史原因以及为了与 C 语言兼容,字符串字面值与标准库 string 类型不是同一种类型
    判断:
#include <typeinfo>
cout<<typeid(var).name()<<endl;
 
    string 类型的输入操作符(cin>>str): 
    •  读取并忽略开头所有的空白字符(如空格,换行符,制表符)。 
    •  读取字符直至再次遇到空白字符,读取终止。
 
20、还有一个有用的 string IO 操作:getline(cin, str)。
    这个函数接受两个参数:一个输入流对象和一个 string 对象。getline 函数从输入流的下一行读取,并保存读取的内容,不包括换行符
    
21、string的函数:str.size(), str.empty(), str[n], str1 == str2。
    注意:str可以调用这些方法,但字面值常量不能调用!!!这与Java不同。且==比较的是内容。
//cout<<"\"字符串\".size() : "<<"字符串".size()<<endl; //error
string str="字符串";
cout<<str.size()<<endl; string str1="字符";
string str2="串"; cout<<((str1+str2) == str)<<endl;
    注意:字面值常量甚至不能相加,因为是字面值常量是const char[n]。        
              字符串可以和字面值常量相加,但是,字面值常量不能和字面值常量相加。。。卧槽。
str2=str1+"abc";    //OK
str2="abc"+str1; //OK
str2="abc"+"abc"+str1; //ERROR,因为前面两个是字面值常量相加。
 
22、string 类类型和许多其他库类型都定义了一些配套类型(companion type)。
    通过这些配套类型,库类型的使用就能与机器无关(machine-independent)
    string size()返回的size_type类型就是这些配套类型中的一种。
 
    str2=str1;类似于Java中的基本类型赋值,而非引用!!!如果想引用,使用string &str2=str1;
 
23、cctype头文件中,都是处理char的函数。
    建议使用c++版本的c标准库头文件。c标准库头文件:ctype.h。而c++版本则是cctype。内容一样,但更适合c++。且c++版本中的名字都这std命名空间中。
 
24、标准库vector类型。我们把 vector 称为容器(类似Java的集合),是因为它可以包含其他对象。一个容器中的所有对象都必须是同一种类型的。
    vector 是一个类模板(class template),所谓模板,类似Java的泛型。
    如:vector<int> ivec; 定义了一个int类型的vector。
    
    vector对象可以在运行时动态的添加元素,效率高。
        虽然可以对给定元素个数的 vector 对象预先分配内存,但更有效的方法是先初始化一个空 vector 对象,然后再动态地增加元素(我们随后将学习如何进行这样的操作)。 
 
    如果 vector 保存内置类型(如 int 类型)的元素,那么标准库将用 0 值创建元素初始化式。
    如果 vector 保存的是含有构造函数的类类型(如 string)的元素,标准库将用该类型的默认构造函数创建元素初始化式。
 
    vector< vector<int> > ivec3(10,ivec1); // 注意,不要写成>>,因为这是操作符 
 
    vector的操作类似string。 还有一个push_back()函数,用于追加元素。
    vector的size()返回的是vector<type>::size_type类型,注意,不是vector,而是vector<type>。
 
    vector的索引操作不会添加元素,只能修改。
 
    除了使用下标来访问 vector 对象的元素外,标准库还提供了另一种访问元素的方法:使用迭代器(iterator)。
    标准库为每一种标准容器(包括 vector)定义了一种迭代器类型。迭代器类型提供了比下标操作更通用化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有的容器都适用,现代 C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的 vector 类型也是这样。 
    每种容器类型还定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素,但不能改变其值。 
for(vector<int>::const_iterator iter=ivec.begin(); iter!=ivec.end();iter++){
//*const_iterator, only for read
}

注意const_iterator和 const iterator是不同的。

 
    任何改变 vector 长度的操作都会使已存在的迭代器失效。
 
25、bitset
#include <iostream>
#include <string>
#include <bitset> using namespace std; int main(){
string str="";
// cout<<str.length()<<endl; bitset<> bs(str);
cout<<bs<<endl; bitset<> bi(0xffffffff);
cout<<bi<<endl; bitset<> bs2(str,,);
cout<<bs2<<endl; bitset<> bs3(str,str.size()-);
cout<<bs3<<endl; //**********
cout<<bs3.any()<<endl; //是否有1
cout<<bs3.none()<<endl; //是否无1
cout<<bs3.count()<<endl; //1的个数
cout<<bs3.size()<<endl; //二进制的位数 cout<<bs3.test()<<endl; //该位是否1
cout<<bs3.set()<<endl; //全设为1
cout<<bs3.reset()<<endl;//全设为0 cout<<bs3.set()<<endl;//注意,是高位
cout<<bs3.reset()<<endl; cout<<bs3.flip()<<endl; //按位取反
cout<<bs3.flip()<<endl; //按位取反 cout<<bs3.to_ulong()<<endl; //转成ulong cout<<bs3<<endl; //将位输出到流 return ;
}
#include <iostream>
#include <string>
#include <vector>
#include <bitset> using namespace std; int main(){ vector<int> ivec;
ivec.push_back();
ivec.push_back();
ivec.push_back();
ivec.push_back();
ivec.push_back();
ivec.push_back();
ivec.push_back(); bitset<> bs; for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();iter++){
bs.set(*iter); //flip() works for this
}
cout<<bs<<endl; return ;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26、总的来说,相对于 C++ 内置数据类型的数组指针而言,程序员应优先使用标准库类类型。 
 
 
《《《未完待续》》》