您的位置:首页 > 编程语言 > C语言/C++

重学C++ (一) 变量和基本类型、标准库类型

2016-01-10 18:17 381 查看

前言

大一的时候我就已经学过C++程序设计了,但是我从来不敢跟别人说我会C++。

事实上,平时里大多数时候我用的是C++里面C的部分,偶尔用一下类来封装(其实用struct也可以实现的)。

对于C++的特点,我一直没有很好的去学习,只是略知一二,所以在实际使用中常常会出现一些难以察觉的问题。

我举个很简单的例子,我们打算输出一个vector内的元素:

[code]#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> vc;
    vc.push_back(1);
    vc.push_back(2);
    for (int i = -1; i < vc.size()-1; i++)
        cout<<vc[i+1]<<endl; 

    return 0;
}


也许之前我们会以为这段代码完美无错,然而事实并非我们想的那样,结果是什么都没输出。

问题出在哪?如果你明白size()返回的是size_type类型,而该类型是unsigned的,那么就不会犯这种错误了。(同样的问题会出现在sizeof上)

另外,C++中main的返回值必须是int你是否知道?大部分iterator只能自增自减而不能像 i+=n 这样赋值?

C++的面向对象思想,范式编程,STL的源码实现,我都只是停留在“了解”的层面,这远远是不够的。

通常,只有在“熟悉”的情况下,我才敢将其写到简历上,跟别人说。

很多原因,促使我想要重新来学习C++,之前读过《C++ primer》,但是并没有读完,而且读到越后面越没耐心。

这一次,我希望自己能够一步一个脚印,把这本书啃下来,并将一些零散的,我自己认为需要注意的点在blog中记录下来,这也是敦促自己学习的一种方式。

与此同时,我将开始阅读STL的部分源码,很期待能够获取新知。

第一章 快速入门

1.对于main函数,返回类型必须是int型,返回值是一个状态指示器,0表示成功,非0由操作系统定义。UNIX中可以通过下面命令获取状态:

[code]$ echo $?


2.标准库定义了4个IO对象:cin,cout,cerr,clog;(cerr默认不缓冲,clog默认带缓冲)

3.输出操作返回的值是输出流本身:

[code]std:cout<<"Hello "<<"world"<<std:endl;


能够执行是因为std:out<<”Hello “返回其左操作数std:out;

所以以上语句等价于:

[code]std:cout<<"Hello ";
std:cout<<"world";
std:cout<<std:endl;


4.使用输出语句的时候应该注意刷新输出流,忘记刷新输出流将可能导致输出停留在缓冲区中。(换行可用于刷新输出流);

5.如果不能保证读取变量前重置变量,就应该初始化变量;

6.代码改变时,注释应该与代码保持一致,错误的注释比没有注释更糟糕;(所以我们应该尽可能提高代码的可读性,而不是依赖于注释!)

7.注释不可嵌套!

8.Unix系统中通常用Control-d来输入文件结束符(windows下是Control-z);

第二章 变量和基本类型

1.char类型通常是单个机器字节(byte),wchar_t(宽字符)类型用于拓展字符集,比如汉子和日语,这些字符集中一些字符不能用单个char表示;

2.在C++中,把负值赋给unsigned对象是完全合法的,结果是该负数对该类型的取值个数求模后的值,比如-1赋给8位的unsigned char,结果是255;

使用unsigned可以避免值越界导致结果为负数的可能性。

3.float只能保证6位有效数字,而double至少保证10位;

4.char类型是整型,但是char通常用于存储字符而不用于计算(因为不同实现中char可能是unsigned或signed);

5.只有内置类型存在字面值,字面值常量的值是不能修改的(它存放在代码段中)。

[code]#include <iostream>
using namespace std;

int main()
{
    char *ptr = "Linux";
    *ptr = 'T';
    cout<<ptr<<endl;
    return 0;
}


上面程序将造成seg-fault或者崩溃,因为字面值常量”Linux”是在只读代码段中,对它的修改操作是失败的;

6.以0开头的字面值整数常量表示八进制,0x或0X开头则表示十六进制;

** int month = 09; 将会报错,因为八进制没有09这个值;

7.没有short类型的字面值常量;

8.默认的浮点字面值常量为double类型;

9.为兼容C,C++所有字符串字面值都由编译器自动在末尾添加一个空字符;

10.字符串字面值的连接:

[code]std::cout<<"a multi-line "
          "string literal "
          "using concatenation"
        <<std::endl;


输出: a multi-line string literal using concatenation

**字符串字面值和宽字符串字面值不能连接,是未定义的。

11.多行字面值

在一行的末尾加一个反斜杠可将此行和下一行当作同一行处理:

[code]std::co\
out << "Hi" << std::en\
dl;


**反斜杠必须是该行的尾字符,其后不能有注释或空格;

**后继行行首任何空格和制符表都是字符串字面值的一部分,所以后继行不能有正常的缩进。

12.左值:可以出现在赋值语句的左边或右边;

右值:只能出现在赋值的右边;

比如:变量是左值,数字字面值是右值。

13.建议:标识符不要包含两个连续的下划线,也不要以下划线开头后面紧跟一个大写字母(保留,用于标准库);

14.初始化不是赋值。初始化指创建变量并给他赋初始值,而赋值则是擦除对象的当前值并用新值代替;

15.内置类型的全局变量会初始化为0,局部变量不自动初始化;

作为习惯,我们都应该对变量进行初始化;

事实上,未初始化变量在内存中的某个位置,该位置有一个值,为上次使用余留下来的。

16.声明和定义:

变量的定义:为变量分配存储空间,还可以指定初始值,在一个程序中,变量有且只有一个定义;

声明:用于向程序表明变量的类型和名字;

可以通过使用extern关键字声明变量名而不定义它,也不分配存储空间;

定义也是声明:定义变量时我们声明了它的类型和名字;

声明也是定义,除非使用extern来说明不定义它;

如果声明有初始化式,它可被当作定义,即使有extern标记(只有当extern声明位于函数外部时,才可以含有初始化式);

[code]extern double pi = 3.14; //ok:定义
extern double pi; //ok:声明
extern double pi = 3.14; //error:重定义


17.因为常量在定义后就不能被修改,所以定义时必须初始化;

18.非const变量默认为extern,而要使const变量能够在其他文件访问, 必须显式指定为extern:

[code]//file1.cc
int counter; //定义

//file2.cc
extern int counter; //声明
++counter; //使用


[code]//file1.cc
extern const int size = MAXN; //定义

//file2.cc
extern cosnt int size; //声明
int ssize = size; //使用


19.不能定义引用类型的引用;

引用必须用与该引用同类型的对象初始化;

引用初始化后,不能将引用绑定到另一个对象(这与指针不同);初始化时指明引用指向哪个对象的唯一方法;

引用不占用内存,而指针需要占用一个指针类型的内存;

const引用是指指向const对象的引用;

const引用可以绑定到不同但相关类型的对象或绑定到右值!

原因:

[code]double dval = 3.14;
const int &ri = dval;


以上代码编译器将转换为:

[code]double dval = 3.14;
int tmp = dval;
const int &ri = tmp;


如果ri是非const,那么对ri的修改将修改的是tmp而不是dval,所以对于非const是禁止的,而对于const,因为其本身就不能修改,所以就没必要禁止。

20.枚举成员是常量,且不要求唯一:

[code]//point2d = 2, point2w = 3, point3d = 3, point3w = 4;
enum Points {point2d = 2, point2w,
             point3d = 3, point3w};


21.struct默认成员是public,而class默认是private;

22.头文件用于声明而不是用于定义;

因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义;

允许const变量定义在头文件中,因为const对象默认定义为它的文件的局部变量,所以包含该头文件的源文件都有自己的const变量;另外,如果const变量不是用常量表达式初始化,那么它不应该在头文件中定义,否则,该变量应该在一个源文件中定义并初始化,然后在头文件中添加extern共享;

第三章 标准库类型

1.在头文件中必须使用【完全限定】的标准库名字:因为预处理器会将头文件复制到程序中,如果头文件中使用了using声明,则包含该头文件的每个程序都放置了同一using声明,然而程序并不一定需要using声明;

2.一个有用的string IO操作:getline,它接收一个输入流和一个string对象;它读取的内容不包含换行符,getline函数将istream参数作为返回值;

3.string的size操作返回的是string::size_type类型,不要把它赋给一个int变量!

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

[code]string s1 = "hello" + ","; //error
string s2 = s1 + "," + " world"; //ok
string s3 = "hello" + "," + s2; //error


5.string的索引下标类型为unsigned的size_type;

6.string对象中字符的处理:头文件cctype;

7.cname头文件定义在名字空间std内,而name.h则不是;

8.vector不是一种数据类型,而是一个类模板,vector< int>才是数据类型;

9.使用vector的size_type类型时:

[code]vector<int>::size_type; //ok
vector::size_type; //error


10.如果循环中插入或者删除元素,那么注意循环判断条件中size的变化;

11.迭代器操作:==或!=; 对其他操作如四则运算,大小比较需要谨慎;

12.const_iterator:只能用于读取容器内的元素,不能改变其值;

对const_iterator解引用,返回的是一个const值;

13.vector迭代器除自增自减,还支持以下操作(其他迭代器不一定支持):

[code]iter+n;
iter-n;
iter1-iter2;


14.任何改变vector长度的操作都会使已存在的迭代器失效!

15.定义bitset时,要明确bitset含有多少位,长度必须为整型字面值常量或者已用常量值初始化的整型const对象;

16.bitset的count操作返回类型是size_t,包含在cstddef头文件中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: