C++ primer 详解(第三章)

3.1 命名空间的using声明

1.在初学C++的时候,写的第一条代码便是在控制台输出一个Hello Word,而在#include后面的第一句话便是using namespace std;,那时候只知道不写这句代码就会报错,但是不知为什么。这句话的含义是:这个文件会使用到std标准库中的名字。

2.using声明有两种方式:

using namespace std; //这种是最方便的,但是可能会包含太多

using std::cout;  //这种是指定需要使用std中哪个名字

3.注意头文件中不应该包含using声明,因为头文件被包含的时候,会将里面的内容拷贝到引用它的文件里面去,如果头文件中有using声明,那么每个使用了该头文件的文件都会有这个声明,在一些程序中,可能会引起一些名字冲突。

3.2标准库类型string

3.2.1定义和初始化string对象

默认初始化:string s1;  //使用string类默认初始化,s1被初始化为空

拷贝初始化:string s2=“value”;

直接初始化:string s3(“value”);

每个值都相同的直接初始化:string s4(10,’c’);

每个值都想同的拷贝初始化:string s5=string(10,’c’);

2.拷贝初始化和直接初始化的区别:在转载的一篇博客上有详解,这里大概说一下:拷贝初始化会首先生成一个临时对象,然后调用拷贝构造函数,将临时对象拷贝到需要复制的那个对象中。而直接初始化是直接调用与实参所匹配的构造函数。

3.2.2sting对象上的操作

1.可读写:string对象重载了<<操作符,可以使用cout<<在控制台打印string对象,(在这里说明一下使用cout,cin一个string对象和一个char数组的区别,string对象在创建的时候就已经保存了长度,而且在输出的时候也是根据string对象所保存的长度进行输出,这和char数组的输出不一样。)

2.string对象在使用cin>>string赋值的时候,会忽略掉开头的空格,结束于下一个空格。

3.可以使用getline获取一行string对象,直到遇到回车符(回车符最后会被去掉)

4..empty()(判断是否为空)操作和.size()(获取string字符的个数)。size返回的是string::type_size类型,只知道它是一个无符号类型,不要和int类型混用。

5.string对象可以相互比较,比较规则:首先如果长度和对应的1字母都相同,那么这两个string对象就是完全相同的,然后如果字母有不相同的那么就按字典序比较第一对不同的字母(字典排序越靠后越大),如果字母完全相同就比较长度。

6.string对象可以用“+”拼接。

7.对string对象中的元素进行处理:

1.遍历使用for(auto c :str) cout<<c<<endl;

(这里的for应该把它当成一个函数来使用,因此若想在循环里面改变c的值,则使用(for(auto &c:str)))

2.如果不想使用遍历,而是只处理一部分字符,可以使用下标运算符:[];

3.3Vector

Vector的英文翻译为向量,在这里作为容器。

Vector是应用的模版实现,因此每次在使用的时候需要具体类型实例化后使用。

3.3.1定义和初始化vector对象

默认初始化:vector<T> v1;

拷贝初始化:vector<T> v2=v1;

直接初始化:vector<T> v3(v2);

指定含有多个不相同初始值的初始化:vector <T> v4{a,b,c,d};

指定含有多个相同初始值的初始化:vector<T> v5(10,-1);

  1. 在vector中,圆括号只用于:
    1. 以一个vector对象直接初始化另外一个对象。
    2. 指定元素的个数

而列表初始化用于:

  1. 若花括号里面是该vector是被实例化的类型,则用来初始化。
  2. 若花括号里面不是该vector对象类型,编译器会尝试以圆括号的方式解析。

3.3.3对Vector的一些操作

1..push_back()向尾端添加一个元素

2.vector也能使用for循环遍历

3.vector的大小类型:vector<int>::size_type.

4.vector也可以比较大小,规则和string一样。

5.只能用下表操作符访问已经存在的元素,需要添加元素等则使用push_back();

3.4迭代器Iterator

迭代器性质和指针差不多,因此可以按照指针的性质来理解

3.4.1使用迭代器

1.V.begin()和V.end(),其中V.begin()返回一个指向容器第一个元素的迭代器,V.end()返回指向最后一个元素的下一个位置。

2.如果容器为空,则begin()=end().=。

3.如果不明白begin()和end()的返回值,可以用auto。

4.迭代器也可以使用*操作符解引用,也可以使用++运算符。

5.在条件运算中,对于容器来说尽量使用!=代替大于或小于,因为有些容器并没有定义大于小于,但是定义等于和不等于确很简单。

6.理解const_iterator与const iterator与typedef定义的类型一样,const iterator const修饰的时iterator这个指针,因此说明的这个指针是常量。而单独定义的const_iterator是指所指对象是const的。

7.begin()和end()若对象是普通对象返回的也是一个普通迭代器,若对象是常量,返回const_iterator。若想一直得到一个指向这个对象,但是不修改这个对象的值的指针,用cbegin();

8.使用指针访问成员函数操作符“->”的由来:使用指针,需要解引用:*it,再使用点操作符,但是由于优先级的原因,需要打上括号:(*it).begin();为了简便,把括号加*操作符符加点操作符简化为“->”;it->begin();

9.指针可以使用++运算符来移动一个类型的位子。比如一个int类型,++一次,移动4字节(在int占32位的情况下。)

10.谨记:但凡使用了迭代器的循环体,都不能改变这个容器中元素的个数。

3.4.2迭代器的运算

1.迭代器之间可以相减,得到的结果两个元素之间的距离(可以为负数)

2.迭代器也可以和指针一样,使用++操作符移动一个元素之间的距离。

3.5数组

1.数组和Vector有差不多的性质,可以使用下标随机访问,与Vector不同的是,数组的大小是确定的,也不能随意改变它的大小,性能高但是不灵活。

2.对于Vector和数组的选择,如果不清楚元素的确切个数,选择使用vector,如果能够确定元素个数的话,使用数组。

3.5.1 定义和初始化内置数组

int arr[42]   //含有42个整形元素的数组;

int *arr[10]  //含有10个整型指针的数组

  1. 数组里面的元素个数[]也可以用常量代替,但是必须是编译时常量,也就是常量表达式。

例如:const int sz=5;

     int arr[sz];

是可以的。

  1. 数组和内置类型一样,如果在函数内部定义了一个数组,那么这个数组会被默认初始化为未定义的值。
  2. 显式初始化数组:可以对数组使用列表初始化{},在带有显示初始化数组定义中,可以不指定数组的大小,编译器会根据初始值的数量自己计算并推测出来。
  3. 字符数组也可以使用花括号初始化,但是一定注意后面需要留一个位置给‘\0’

char a1[]={‘C’,’+’,’+‘,‘\0’};

char a2[]=”C++”;  //a2的维度为4;

char a3[3]=”C++”;//错误:”C++”,这个字符串是4个元素。

char a4[]={‘C’,’+’,’+’}; //这种定义是允许的,但是后面会有一系列的问题。

  1. 不允许将数组的内容拷贝给其他数组作为初始值,也不能用数组为其他数组赋值:

int a[]={0,1,2};

int a2[]=a;   //错误,不允许将一个数组作为初始值给另外一个数组

a2=a;      //错误,不能把一个数组直接赋值给另外一个数组

  1. 理解复杂的数组声明

   int *ptrs[10];  //这个数组有10个元素,每个元素都是int指针。

int &refs[10];  //错误,数组里面应该是对象,不能是引用。(引用不占空间)

int (*Parray)[10]=&arr; //Parray指向一个含有10个int类型的元素的数组。

int (&arrRef)[10]=arr; //arrRef引用一个含有10个整型元素的数组。

   int *(&arry)[10]=ptrs;//arry引用一个含有10个指向int类型的指针的数组

//在编译器上试了一下把括号去掉的效果

int *&array[10]=ptrs;编译器会报错了,报错内容是不允许定义引用的数组。

继续测试: int **array[10];这样的数组初始化必须是{ptr1,ptr2,ptr3…};

因此如果对于定义数组,不打括号的话,数组里面的元素应该是数组名字左边的所有声明符加起来。

 

书上说,对于数组的理解应该按照由内到外理解。但是按照之前对指针类型的分析,这里可以这样分析。首先看这个数组里面的元素类型是什么:int *ref[10];

可以这样看(int *)ref[10];也就是说这个数组里面的元素都是指针。而

int (*Parray)[10]=&arr;这种类型,首先数组里面的元素是int型的,再看parray它就是一个指针了。指向这个数组。

 

3.5.2访问数组元素

1.和其他数组元素一样,数组也有自己独有的下标类型:size_t类型,这是一种和机器相关的无符号类型。

2.与vector相比,数组的下标类型可以是有符号类型,也可以是无符号类型,但是vector的下标必须是无符号类型。

3.5.3指针和数组

在C++中,指针和数组有很多紧密的联系。在很多时候,使用数组的时候,编译器会将它转换为指向数组首元素的指针。

  1. 使用数组名初始化auto和decltype关键字的不同:

int ia[]={0,1,2,3,4,5,6,7,8,9};

auto ia2(ia);  //ia是一个整型指针,指向ia的第一个元素

decltype(ia) ia3={0,1,2,3,4,5,6,7,8,9}; //ia是一个数组。

//思考为什么:因为ia2是用数组初始化的,前面提到不能用数组初始化一个数组,因此auto会将其转化为指针。

  1. 与其他容器一样,数组也有迭代器,数组的迭代器是指针。
  2. C++11标准中可以使用begin和end函数得到指向数组的头指针和尾元素的下一个指针。
  3. 指向数组元素的指针的运算和其他容器有一样的性质。可以加一个整数,可以想减,想减结果是一个有符号类型。
  4. 一个指针指向的数组类型,也可以使用下标运算符。

3.5.4C风格字符串

1.C风格字符串:以‘\0‘结尾的字符串。

2.C风格字符串的函数:strlen(p),strcmp(p1,p2);strcat(p1,p2);strcpy(p1,p2);

3.在很多编译器中,使用以上函数会出现waring或者报错,这是因为以上函数都不会去验证这个字符串是否是标准的C风格字符串。也就是如果这个字符串不是以‘\0’结尾,它会沿着这个字符串所在的内存一直向后找,直到找到‘\0’;

4.对于大多数情况,字符串使用string会更加的安全。

3.5.5与旧代码的接口

1.版本总是会兼容以前的内容,因此string可以很容易转换为C风格字符串。使用.c_str();就可以,但是这个函数只是返回一个C风格字符串,而没有改变它本身,因此想一直使用它返回的字符串,最好拷贝下来。

2.想将C风格字符串转换为string类型,可以直接用C风格字符串初始化string类型。

3.数组也可以用来初始化vector。传入头指针和尾指针即可:

 int int_arr[]={0,1,2,3,4};

vector<int> ivec(begin(int_arr),end(int_arr));

3.6多位数组

对于多维数组,没什么好说的,只要记得,多位数组仅仅是数组里面的元素是数组,其他就都很好理解了。

可以使用类型别名简化多位数组的指针

using int_array=int[4];

typedef int int_attay[4];

int_array *p;//p是一个二维数组的指针。

 

posted @ 2016-12-08 16:00  Super_cpp  阅读(705)  评论(0编辑  收藏  举报