身为C++的零基础初学者,短期内把《C++Primer》啃下来是一个比较笨但是有效的方法,一方面可以掌握比较规范的C++语法(避免被项目中乱七八糟的风格带跑偏),另一方面又可以全面地了解C++语法以及C++11新标准(后续要做的事情就剩下查漏补缺,不断完善自己的知识体系)。
个人感觉从零学习一门新知识比较好的方法是快速了解知识的全貌,然后构建自己的知识地图,后续不断地补充相应的细节。
由于《C++Primer》和大多数的教科书一样废话连篇,因此想要精炼一下每篇文章的内容再打印成pdf,方便温故知新。
byte
:可寻址的最小内存块,大多数机器的字节由8
比特构成float
由32
位来表示,double
由64
位来表示;一般float
和double
分别有7
和16
个有效位int
执行整数运算(因为short
太短而long
一般与int
有相同的尺寸),如果你的数值超过了int
的表示范围则选用long long
double
,一方面是因为float
精度不够,另一方面是因为双精度浮点数和单精度浮点数的计算代价相差无几8
比特大小的unsigned char
可以表示0~255
,如果我们将-1
赋给它将会得到255
int
值时,int
型的变量会被转化为无符号类型,结果可能是出乎意料的:// 切勿混用带符号类型和无符号类型
unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // -84
std::cout << i + u << std::endl; // 如果int占32位则输出4294967264
20
十进制;024
八进制;0x14
十六进制;3.14159E0
浮点型'a'
表示一个字符;"a"
字符串字面量包含字母a
和空字符\0
initialized
C++11
新标准的一部分,用花括号来初始化变量,这种方法有一定的优势:当使用列表初始化且初始值存在丢失信息的风险时则编译器将报错0
,但是定义在函数体内部的内置变量将不被初始化
C++
支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可独立编译。
为了支持分离式编译,C++
将定义和声明区分开。其中声明规定了变量的类型和名字,定义除此功能外还会申请存储空间并可能为变量赋一个初始值。如果想声明一个变量而非定义它,就使用关键字extern
并且不要显式地初始化变量:
变量能且仅能被定义一次,但是可以被多次声明。
extern int i; // 声明i而非定义i
extern int i = 1; // 定义i, 这样做抵消了extern的作用
C++11
中新增了“右值引用”,而我们这里讲的引用指的是“左值引用”。
指针只可能是以下四种情况:
试图拷贝或者以其他方式访问无效指针的值都会引发错误,编译器并不会负责检查此类错误。空指针不指向任何对象,在试图使用一个指针之前最好先判断它是否为空。C++11
中得到空指针最直接的方法就是字面值nullptr
。
建议:初始化所有指针。访问未经初始化的指针相当于去访问一个本不存在的位置上本不存在的对象。如果指针所占空间中恰好有内容,而这些内容又被当做某个地址。我们就很难分清它是否是合法的了。因此建议初始化所有指针,并且尽量等定义了对象之后再定义指向它的指针。如果实在不清楚指针应该指向何处,就将它初始化为
nullptr
,这样程序就能检测并在非法引用时报错。
const
对象一旦创建后其值就不能再改变,所以const
对象必须初始化。
如果想在多个文件之间共享
const
对象,那么必须在变量的定义之前添加extern
关键字。
默认状况下,const
对象仅在文件内有效。const int bufSize = 512;
以编译时初始化的方式定义一个const
对象时,编译器将在编译过程中把用到该变量的地方都替换成对应的值。如果我们希望只在一个文件中定义const
然后在其他多个文件中声明并使用它。解决的方法是对于const
变量无论是声明还是定义都使用extern
关键字,这样就仅需定义一次了。
// file_1.cc 定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufSize = fcn();
// file_1.h 头文件
extern const int bufSize; // 与file_1.cc中定义的常量是同一个
与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。
const int c1 = 1024;
const int &r1 = c1;
int errNum = 0;
int *const curErr = &errNum; // curErr会一直指向errNum
const double pi = 3.14159;
const double *const pip = π // pip是一个指向常量对象的常量指针
指针本身是一个对象,它又可以指向另一个对象。因此指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。顶层
const
表示指针本身是一个常量,底层const
表示指针所指的对象是不是一个常量。
当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const
资格。
常量表达式
const expression
是指值不会改变并且在编译过程就能得到计算结果的表达式。
C++11
新标准规定,允许将变量声明为constexpr
类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr
的变量一定是一个常量,且必须用常量表达式初始化。
类型别名
type alias
是一个名字,它是某种类型的同义词。它让复杂的类型名字变得简单明了、易于理解和使用。
// 传统方法
typedef double wages; // wages是double的同义词
// 新标准
using SI = Sales_item
C++11
引入了auto
类型说明符,可以让编译器通过初始值来推断变量的类型。需要注意的是,编译器推断出来的auto
类型有时候与初始值的类型并不完全一样,编译器会适当地改变结果类型使其更加符合初始化规则。
atuo
一般会忽略掉顶层const
,底层const
会保留下来,比如当初始值是一个指向常量的指针。如果希望推断出的auto
类型是一个顶层const
时,需要明确指出:const int ci = i;
const auto f = ci;
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data accum, trans, *salesptr;
为了确保各个文件中类的定义一致,类通常被定义在头文件中,而且类所在头文件的名字应与类的名字一样。
const
和constexpr
变量等C++
会使用头文件保护符来防止包含多份相同的头文件。#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
#endif
C++
将C
语言中的头文件如name.h
重命名为cname
,即去掉了.h
后缀同时在前面加上字母c
。一般而言C++
程序员应该使用cname
的头文件而非name.h
的形式,标准库中的名字总能在命名空间std
中找到,如果使用name.h
则程序员不得不时刻牢记从属于C
还是C++
。