目录

第一章 开始

熟悉编译器

IO

注释

while语句

for语句

使用文件重定向

第二章 变量和基本类型

基本内置类型

如何选择类型

类型转换

字面值常量

变量

变量定义(define)

变量的声明(declaration) vs 定义(define)

左值和右值

复合类型

引用

指针

const限定符

初始化和const

const的引用

指针和const

顶层const

constexpr和常量表达式(▲可选)

处理类型

类型别名

auto类型说明符 c++11

decltype类型指示符

自定义数据结构

struct

编写自己的头文件

第三章 字符串、向量和数组

using声明

string

定义和初始化string对象

string对象上的操作

处理string对象中的字符

vector

定义和初始化vector对象

向vector对象中添加元素

其他vector操作

迭代器iterator

使用迭代器

迭代器运算

数组

定义和初始化内置数组

访问数组元素

数组和指针

C风格字符串

多维数组

指针vs引用

指向指针的指针

动态数组

第四章 表达式

表达式基础

算术运算符

逻辑运算符

赋值运算符

递增递减运算符

混用解引用和递增运算符

成员访问运算符

条件运算符

位运算符

sizeof运算符

逗号运算符

类型转换

隐式类型转换

显式类型转换(尽量避免)

运算符优先级表

第五章 语句

简单语句

条件语句

迭代语句

跳转语句

try语句块和异常处理

第六章 函数

函数基础

局部对象

函数声明

参数传递

传值参数

传引用参数

const形参和实参

数组形参

main处理命令行选项

可变形参

返回类型和return语句

无返回值函数

有返回值函数

返回数组指针

函数重载

特殊用途语言特性

默认实参

内联(inline)函数

constexpr函数

调试帮助

函数匹配

函数指针


第一章 开始

熟悉编译器

g++

  • 编译:g++ --std=c++11 ch01.cpp -o main

  • 运行:./prog1

  • 查看运行状态:echo $?

  • 编译多个文件:g++ ch2.cpp Sales_item.cc -o main

输入 g++ --help,查看编译器选项:

Usage: g++ [options] file...
Options:-pass-exit-codes         Exit with highest error code from a phase--help                   Display this information--target-help            Display target specific command line options--help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]Display specific types of command line options(Use '-v --help' to display command line options of sub-processes)--version                Display compiler version information-dumpspecs               Display all of the built in spec strings-dumpversion             Display the version of the compiler-dumpmachine             Display the compiler's target processor-print-search-dirs       Display the directories in the compiler's search path-print-libgcc-file-name  Display the name of the compiler's companion library-print-file-name=<lib>   Display the full path to library <lib>-print-prog-name=<prog>  Display the full path to compiler component <prog>-print-multiarch         Display the target's normalized GNU triplet, used asa component in the library path-print-multi-directory   Display the root directory for versions of libgcc-print-multi-lib         Display the mapping between command line options andmultiple library search directories-print-multi-os-directory Display the relative path to OS libraries-print-sysroot           Display the target libraries directory-print-sysroot-headers-suffix Display the sysroot suffix used to find headers-Wa,<options>            Pass comma-separated <options> on to the assembler-Wp,<options>            Pass comma-separated <options> on to the preprocessor-Wl,<options>            Pass comma-separated <options> on to the linker-Xassembler <arg>        Pass <arg> on to the assembler-Xpreprocessor <arg>     Pass <arg> on to the preprocessor-Xlinker <arg>           Pass <arg> on to the linker-save-temps              Do not delete intermediate files-save-temps=<arg>        Do not delete intermediate files-no-canonical-prefixes   Do not canonicalize paths when building relativeprefixes to other gcc components-pipe                    Use pipes rather than intermediate files-time                    Time the execution of each subprocess-specs=<file>            Override built-in specs with the contents of <file>-std=<standard>          Assume that the input sources are for <standard>--sysroot=<directory>    Use <directory> as the root directory for headersand libraries-B <directory>           Add <directory> to the compiler's search paths-v                       Display the programs invoked by the compiler-###                     Like -v but options quoted and commands not executed-E                       Preprocess only; do not compile, assemble or link-S                       Compile only; do not assemble or link-c                       Compile and assemble, but do not link-o <file>                Place the output into <file>-pie                     Create a position independent executable-shared                  Create a shared library-x <language>            Specify the language of the following input filesPermissible languages include: c c++ assembler none'none' means revert to the default behavior ofguessing the language based on the file's extension

输入 g++ -v --help可以看到更完整的指令。 例如还有些常用的:

-h FILENAME, -soname FILENAME: Set internal name of shared library
-I PROGRAM, --dynamic-linker PROGRAM: Set PROGRAM as the dynamic linker to use
-l LIBNAME, --library LIBNAME: Search for library LIBNAME
-L DIRECTORY, --library-path DIRECTORY: Add DIRECTORY to library search path

获得程序状态:

  • windows: echo %ERRORLEVEL%

  • UNIX: echo $?

IO

  • #include <iostream>

  • std::cout << "hello"

  • std::cin >> v1

记住>><<返回的结果都是左操作数,也就是输入流和输出流本身。

endl:这是一个被称为操纵符(manipulator)的特殊值,效果是结束当前行,并将设备关联的缓冲区(buffer)中的内容刷到设备中。

UNIX和Mac下键盘输入文件结束符:ctrl+d,Windows下:ctrl+z

头文件:类的类型一般存储在头文件中,标准库的头文件使用<>,非标准库的头文件使用""。申明写在.h文件,定义实现写在.cpp文件。

避免多次包含同一头文件

#ifndef SALESITEM_H
#define SALESITEM_H
// Definition of Sales_itemclass and related functions goes here
#endif

成员函数(类方法):使用.调用。

命名空间(namespace):使用作用域运算符::调用。

注释

  • 单行注释: //

  • 多行注释: /**/。编译器将/**/之间的内容都作为注释内容忽略。注意不能嵌套。

    #define SALESITEM_H
    /*
    * 多行注释格式
    * 每一行加一个*
    */

while语句

循环执行,(直到条件(condition)为假。

for语句

循环头由三部分组成:

  • 一个初始化语句(init-statement)

  • 一个循环条件(condition)

  • 一个表达式(expression)

使用文件重定向

./main <infile >outfile

第二章 变量和基本类型

任何常用的编程语言都具备一组公共的语法特征,最基本的特征包括:

  • 整型、字符型等内置类型

  • 变量,用来为对象命名

  • 表达式和语句,用于操作上述数据类型的具体值

  • if 或 while 等控制结构,有选择地执行一些语句或重复地执行一些语句

  • 函数,用于定义可供随时调用的计算单元

大多数编程语言通过两种方式来进一步补充其基本特征:

  • 自定义数据类型,实现对语言的扩展

  • 将一些有用的功能封装成库函数

基本内置类型

基本算数类型

类型 含义 最小尺寸
bool 布尔类型 8bits
char 字符 8bits
wchar_t 宽字符 16bits
char16_t Unicode字符 16bits
char32_t Unicode字符 32bits
short 短整型 16bits
int 整型 16bits (在32位机器中是32bits)
long 长整型 32bits
long long 长整型 64bits (是在C++11中新定义的)
float 单精度浮点数 6位有效数字
double 双精度浮点数 10位有效数字
long double 扩展精度浮点数 10位有效数字

如何选择类型

  • 1.当明确知晓数值不可能是负数时,选用无符号类型;

  • 2.使用int执行整数运算。一般long的大小和int一样,而short常常显得太小。除非超过了int的范围,选择long long

  • 3.算术表达式中不要使用charbool

  • 4.浮点运算选用double

类型转换

  • 非布尔型赋给布尔型,初始值为0则结果为false,否则为true。

  • 布尔型赋给非布尔型,初始值为false结果为0,初始值为true结果为1。

字面值常量

  • 一个形如42的值被称作字面值常量(literal)。

    • 整型和浮点型字面值。

    • 字符和字符串字面值。

      • 使用空格连接,继承自C。

      • 字符字面值:单引号, 'a'

      • 字符串字面值:双引号, "Hello World""

      • 分多行书写字符串。

        std:cout<<"wow, a really, really long string""literal that spans two lines" <<std::endl;
    • 转义序列。\n\t等。

    • 布尔字面值。truefalse

    • 指针字面值。nullptr

字符串型实际上时常量字符构成的数组,结尾处以'\0'结束,所以字符串类型实际上长度比内容多1。

变量

变量提供一个具名的、可供程序操作的存储空间。 C++变量对象一般可以互换使用。

变量定义(define)

  • 定义形式:类型说明符(type specifier) + 一个或多个变量名组成的列表。如int sum = 0, value, units_sold = 0;

  • 初始化(initialize):对象在创建时获得了一个特定的值。

    • 初始化不是赋值!

    • 初始化 = 创建变量 + 赋予初始值

    • 赋值 = 擦除对象的当前值 + 用新值代替

    • 列表初始化:使用花括号{},如int units_sold{0};

    • 默认初始化:定义时没有指定初始值会被默认初始化;在函数体内部的内置类型变量将不会被初始化

    • 建议初始化每一个内置类型的变量。

变量的声明(declaration) vs 定义(define)

  • 为了支持分离式编译,C++将声明和定义区分开。声明使得名字为程序所知。定义负责创建与名字关联的实体。

  • extern:只是说明变量定义在其他地方。

  • 只声明而不定义: 在变量名前添加关键字 extern,如extern int i;。但如果包含了初始值,就变成了定义:extern double pi = 3.14;

  • 变量只能被定义一次,但是可以多次声明。定义只出现在一个文件中,其他文件使用该变量时需要对其声明。

  • 名字的作用域(namescope){}

    • 第一次使用变量时再定义它

    • 嵌套的作用域

      • 同时存在全局和局部变量时,已定义局部变量的作用域中可用::reused显式访问全局变量reused。

      • 但是用到全局变量时,尽量不适用重名的局部变量。

变量命名规范

  1. 需体现实际意义

  2. 变量名用小写字母

  3. 自定义类名用大写字母开头:Sales_item

  4. 标识符由多个单词组成,中间须有明确区分:student_loan或studentLoan,不要用studentloan。

左值和右值

  • 左值(l-value)可以出现在赋值语句的左边或者右边,比如变量;

  • 右值(r-value)只能出现在赋值语句的右边,比如常量。

复合类型

引用

一般说的引用是指的左值引用

  • 引用:引用是一个对象的别名,引用类型引用(refer to)另外一种类型。如int &refVal = val;

  • 引用必须初始化。

  • 引用和它的初始值是绑定bind在一起的,而不是拷贝。一旦定义就不能更改绑定为其他的对象

指针

int *p; //指向int型对象的指针

  • 是一种 "指向(point to)"另外一种类型的复合类型。

  • 定义指针类型: int *ip1;从右向左读有助于阅读ip1是指向int类型的指针。

  • 指针存放某个对象的地址

  • 获取对象的地址: int i=42; int *p = &i;&取地址符

  • 指针的类型与所指向的对象类型必须一致(均为同一类型int、double等)

  • 指针的值的四种状态:

    • 1.指向一个对象;

    • 2.指向紧邻对象的下一个位置;

    • 3.空指针;

    • 4.无效指针。

    • 对无效指针的操作均会引发错误,第二种和第三种虽为有效的,但理论上是不被允许的

  • 指针访问对象: cout << *p;输出p指针所指对象的数据, *解引用符

  • 空指针不指向任何对象。使用int *p=nullptr;来使用空指针。

  • 指针和引用的区别:引用本身并非一个对象,引用定义后就不能绑定到其他的对象了;指针并没有此限制,相当于变量一样使用。

  • 赋值语句永远改变的是左侧的对象。

  • void*指针可以存放任意对象的地址。因无类型,仅操作内存空间,对所存对象无法访问。

  • 其他指针类型必须要与所指对象严格匹配

  • 两个指针相减的类型是ptrdiff_t

  • 建议:初始化所有指针。

  • int* p1, p2;//*是对p1的修饰,所以p2还是int型

const限定符

  • 动机:希望定义一些不能被改变值的变量。

初始化和const

  • const对象必须初始化,且不能被改变

  • const变量默认不能被其他文件访问,非要访问,必须在指定const定义之前加extern。要想在多个文件中使用const变量共享,定义和声明都加const关键字即可。

const的引用

  • reference to const(对常量的引用):指向const对象的引用,如 const int ival=1; const int &refVal = ival;,可以读取但不能修改refVal

  • 临时量(temporary)对象:当编译器需要一个空间来暂存表达式的求值结果时,临时创建的一个未命名的对象。

  • 对临时量的引用是非法行为。

指针和const

  • pointer to const(指向常量的指针):不能用于改变其所指对象的值, 如 const double pi = 3.14; const double *cptr = &pi;

  • const pointer:指针本身是常量,也就是说指针固定指向该对象,(存放在指针中的地址不变,地址所对应的那个对象值可以修改)如 int i = 0; int *const ptr = &i;

顶层const

  • 顶层const:指针本身是个常量。

  • 底层const:指针指向的对象是个常量。拷贝时严格要求相同的底层const资格。

constexpr和常量表达式(▲可选)

  • 常量表达式:指值不会改变,且在编译过程中就能得到计算结果的表达式。

  • C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量的表达式。

处理类型

类型别名

  • 传统别名:使用typedef来定义类型的同义词。 typedef double wages;

  • 新标准别名:别名声明(alias declaration): using SI = Sales_item;(C++11)

// 对于复合类型(指针等)不能代回原式来进行理解
typedef char *pstring;  // pstring是char*的别名
const pstring cstr = 0; // 指向char的常量指针
// 如改写为const char *cstr = 0;不正确,为指向const char的指针
​
// 辅助理解(可代回后加括号)
// const pstring cstr = 0;代回后const (char *) cstr = 0;
// const char *cstr = 0;即为(const char *) cstr = 0;

auto类型说明符 c++11

  • auto类型说明符:让编译器自动推断类型

  • 一条声明语句只能有一个数据类型,所以一个auto声明多个变量时只能相同的变量类型(包括复杂类型&和*)。auto sz = 0, pi =3.14//错误

  • int i = 0, &r = i; auto a = r; 推断a的类型是int

  • 会忽略顶层const

  • const int ci = 1; const auto f = ci;推断类型是int,如果希望是顶层const需要自己加const

decltype类型指示符

  • 从表达式的类型推断出要定义的变量的类型。

  • decltype:选择并返回操作数的数据类型

  • decltype(f()) sum = x; 推断sum的类型是函数f的返回类型。

  • 不会忽略顶层const

  • 如果对变量加括号,编译器会将其认为是一个表达式,如int i-->(i),则decltype((i))得到结果为int&引用。

  • 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。

  • C++11

自定义数据结构

struct

尽量不要吧类定义和对象定义放在一起。如struct Student{} xiaoming,xiaofang;

  • 类可以以关键字struct开始,紧跟类名和类体。

  • 类数据成员:类体定义类的成员。

  • C++11:可以为类数据成员提供一个类内初始值(in-class initializer)。

编写自己的头文件

  • 头文件通常包含哪些只能被定义一次的实体:类、constconstexpr变量。

预处理器概述:

  • 预处理器(preprocessor):确保头文件多次包含仍能安全工作。

  • 当预处理器看到#include标记时,会用指定的头文件内容代替#include

  • 头文件保护符(header guard):头文件保护符依赖于预处理变量的状态:已定义和未定义。

    • #indef已定义时为真

    • #inndef未定义时为真

    • 头文件保护符的名称需要唯一,且保持全部大写。养成良好习惯,不论是否该头文件被包含,要加保护符。

#ifndef SALES_DATA_H  //SALES_DATA_H未定义时为真
#define SALES_DATA_H
strct Sale_data{...
}
#endif

第三章 字符串、向量和数组

using声明

  • 使用某个命名空间:例如 using std::cin表示使用命名空间std中的名字cin

  • 头文件中不应该包含using声明。这样使用了该头文件的源码也会使用这个声明,会带来风险。

string

  • 标准库类型string表示可变长的字符序列。

  • #include <string>,然后 using std::string;

  • string对象:注意,不同于字符串字面值。

定义和初始化string对象

初始化string对象的方式:

方式 解释
string s1 默认初始化,s1是个空字符串
string s2(s1) s2s1的副本
string s2 = s1 等价于s2(s1)s2s1的副本
string s3("value") s3是字面值“value”的副本,除了字面值最后的那个空字符外
string s3 = "value" 等价于s3("value")s3是字面值"value"的副本
string s4(n, 'c') s4初始化为由连续n个字符c组成的串
  • 拷贝初始化(copy initialization):使用等号=将一个已有的对象拷贝到正在创建的对象。

  • 直接初始化(direct initialization):通过括号给对象赋值。

string对象上的操作

string的操作:

操作 解释
os << s s写到输出流os当中,返回os
is >> s is中读取字符串赋给s,字符串以空白分割,返回is
getline(is, s) is中读取一行赋给s,返回is
s.empty() s为空返回true,否则返回false
s.size() 返回s中字符的个数
s[n] 返回s中第n个字符的引用,位置n从0计起
s1+s2 返回s1s2连接后的结果
s1=s2 s2的副本代替s1中原来的字符
s1==s2 如果s1s2中所含的字符完全一样,则它们相等;string对象的相等性判断对字母的大小写敏感
s1!=s2 同上
<, <=, >, >= 利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)
  • string io:

    • 执行读操作>>:忽略掉开头的空白(包括空格、换行符和制表符),直到遇到下一处空白为止。

    • getline:读取一整行,包括空白符

  • s.size()返回的时string::size_type类型,记住是一个无符号类型的值,不要和int混用

  • s1+s2使用时,保证至少一侧是string类型。string s1 = "hello" + "world" // 错误,两侧均为字符串字面值

  • 字符串字面值和string是不同的类型。

处理string对象中的字符

  • ctype.h vs. cctype:C++修改了c的标准库,名称为去掉.h,前面加c

    如c++版本为cctype,c版本为ctype.h

    • 尽量使用c++版本的头文件,即cctype

cctype头文件中定义了一组标准函数:

函数 解释
isalnum(c) c是字母或数字时为真
isalpha(c) c是字母时为真
iscntrl(c) c是控制字符时为真
isdigit(c) c是数字时为真
isgraph(c) c不是空格但可以打印时为真
islower(c) c是小写字母时为真
isprint(c) c是可打印字符时为真
ispunct(c) c是标点符号时为真
isspace(c) c是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)
isupper(c) c是大写字母时为真
isxdigit(c) c是十六进制数字时为真
tolower(c) c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c) c是小写字母,输出对应的大写字母;否则原样输出c
  • 遍历字符串:使用范围for(range for)语句: for (auto c: str),或者 for (auto &c: str)使用引用直接改变字符串中的字符。 (C++11)

  • str[x],[]输入参数为string::size_type类型,给出int整型也会自动转化为该类型

vector

  • vector是一个容器,也是一个类模板;

  • #include <vector> 然后 using std::vector;

  • 容器:包含其他对象。

  • 类模板:本身不是类,但可以实例化instantiation出一个类。 vector是一个模板, vector<int>是一个类型。

  • 通过将类型放在类模板名称后面的尖括号中来指定类型,如vector<int> ivec

定义和初始化vector对象

初始化vector对象的方法

方法 解释
vector<T> v1 v1是一个空vector,它潜在的元素是T类型的,执行默认初始化
vector<T> v2(v1) v2中包含有v1所有元素的副本
vector<T> v2 = v1 等价于v2(v1)v2中包含v1所有元素的副本
vector<T> v3(n, val) v3包含了n个重复的元素,每个元素的值都是val
vector<T> v4(n) v4包含了n个重复地执行了值初始化的对象
vector<T> v5{a, b, c...} v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5={a, b, c...} 等价于v5{a, b, c...}
  • 列表初始化: vector<string> v{"a", "an", "the"}; (C++11)

向vector对象中添加元素

  • v.push_back(e) 在尾部增加元素。

其他vector操作

vector支持的操作:

操作 解释
v.emtpy() 如果v不含有任何元素,返回真;否则返回假
v.size() 返回v中元素的个数
v.push_back(t) v的尾端添加一个值为t的元素
v[n] 返回v中第n个位置上元素的引用
v1 = v2 v2中的元素拷贝替换v1中的元素
v1 = {a,b,c...} 用列表中元素的拷贝替换v1中的元素
v1 == v2 v1v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同
v1 != v2 同上
<,<=,>, >= 以字典顺序进行比较
  • 范围for语句内不应该改变其遍历序列的大小。

  • vector对象(以及string对象)的下标运算符,只能对确知已存在的元素执行下标操作,不能用于添加元素。

迭代器iterator

  • 所有标准库容器都可以使用迭代器。

  • 类似于指针类型,迭代器也提供了对对象的间接访问。

使用迭代器

  • vector<int>::iterator iter

  • auto b = v.begin();返回指向第一个元素的迭代器。

  • auto e = v.end();返回指向最后一个元素的下一个(哨兵,尾后,one past the end)的迭代器(off the end)。

  • 如果容器为空, begin()end()返回的是同一个迭代器,都是尾后迭代器。

  • 使用解引用符*访问迭代器指向的元素。

  • 养成使用迭代器和!=的习惯(泛型编程)。

  • 容器:可以包含其他对象;但所有的对象必须类型相同。

  • 迭代器(iterator):每种标准容器都有自己的迭代器。C++倾向于用迭代器而不是下标遍历元素。

  • const_iterator:只能读取容器内元素不能改变。

  • 箭头运算符: 解引用 + 成员访问,it->mem等价于 (*it).mem

  • 谨记:但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

标准容器迭代器的运算符:

运算符 解释
*iter 返回迭代器iter所指向的元素的引用
iter->mem 等价于(*iter).mem
++iter iter指示容器中的下一个元素
--iter iter指示容器中的上一个元素
iter1 == iter2 判断两个迭代器是否相等

迭代器运算

vectorstring迭代器支持的运算:

运算符 解释
iter + n 迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。
iter - n 迭代器减去一个证书仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。
iter1 += n 迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1
iter1 -= n 迭代器减法的复合赋值语句,将iter2减n的加过赋给iter1
iter1 - iter2 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。
>>=<<= 迭代器的关系运算符,如果某迭代器
  • difference_type:保证足够大以存储任何两个迭代器对象间的距离,可正可负。

数组

  • 相当于vector的低级版,长度固定

定义和初始化内置数组

  • 初始化:char input_buffer[buffer_size];,长度必须是const表达式,或者不写,让编译器自己推断。

  • 数组不允许直接赋值给另一个数组。

访问数组元素

  • 数组下标的类型:size_t

  • 字符数组的特殊性:结尾处有一个空字符,如 char a[] = "hello";

  • 用数组初始化 vectorint a[] = {1,2,3,4,5}; vector<int> v(begin(a), end(a));

数组和指针

  • 使用数组时,编译器一般会把它转换成指针。

  • 标准库类型限定使用的下标必须是无符号类型,而内置的下标可以处理负值。

  • 指针访问数组:在表达式中使用数组名时,名字会自动转换成指向数组的第一个元素的指针。

C风格字符串

  • 从C继承来的字符串。

  • 用空字符结束(\0)。

  • 对大多数应用来说,使用标准库 string比使用C风格字符串更安全、更高效。

  • 获取 string 中的 cstringconst char *str = s.c_str();

C标准库String函数,定义在<cstring> 中:

函数 介绍
strlen(p) 返回p的长度,空字符不计算在内
strcmp(p1, p2) 比较p1p2的相等性。如果p1==p2,返回0;如果p1>p2,返回一个正值;如果p1<p2,返回一个负值。
strcat(p1, p2) p2附加到p1之后,返回p1
strcpy(p1, p2) p2拷贝给p1,返回p1

尽量使用vector和迭代器,少用数组

多维数组

  • 多维数组的初始化int ia[3][4] = {{0,1,2,3}, ...}

  • 使用范围for语句时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

指针vs引用

  • 引用总是指向某个对象,定义引用时没有初始化是错的。

  • 给引用赋值,修改的是该引用所关联的对象的值,而不是让引用和另一个对象相关联。

指向指针的指针

  • 定义: int **ppi = &pi;

  • 解引用:**ppi

动态数组

  • 使用 newdelete表达和c中mallocfree类似的功能,即在堆(自由存储区)中分配存储空间。

  • 定义: int *pia = new int[10]; 10可以被一个变量替代。

  • 释放: delete [] pia;,注意不要忘记[]

第四章 表达式

表达式基础

  • 运算对象转换:小整数类型会被提升为较大的整数类型

  • 重载运算符:当运算符作用在类类型的运算对象时,用户可以自行定义其含义。

  • 左值和右值

    • C中原意:左值可以在表达式左边,右值不能。

    • C++:当一个对象被用作右值的时候,用的是对象的(内容);

    • 被用做左值时,用的是对象的身份(在内存中的位置)。

  • 求值顺序int i = f1() + f2()

    • 先计算f1() + f2(),再计算int i = f1() + f2()。但是f1和f2的计算先后不确定

    • 但是,如果f1、f2都对同一对象进行了修改,因为顺序不确定,所以会编译出错,显示未定义

算术运算符

  • 溢出:当计算的结果超出该类型所能表示的范围时就会产生溢出。

  • bool类型不应该参与计算

    bool b=true;
    bool b2=-b;   //仍然为true
    //b为true,提升为对应int=1,-b=-1
    //b2=-1≠0,所以b2仍未true
  • 取余运算m%n,结果符号与m相同

逻辑运算符

  • 短路求值:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。先左再右

  • 小技巧,声明为引用类型可以避免对元素的拷贝,如下,如string特别大时可以节省大量时间。

vector<string> text;
for(const auto &s: text){cout<<s;
}

赋值运算符

  • 赋值运算的返回结果时它的左侧运算对象,且是一个左值。类型也就是左侧对象的类型。

  • 如果赋值运算的左右侧运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型。

  • 赋值运算符满足右结合律,这点和其他二元运算符不一样。 ival = jval = 0;等价于ival = (jval = 0);

  • 赋值运算优先级比较低,使用其当条件时应该加括号。

  • 复合赋值运算符,复合运算符只求值一次,普通运算符求值两次。(对性能有一点点点点影响) 任意复合运算符op等价于a = a op b;

递增递减运算符

  • 前置版本j = ++i,先加一后赋值

  • 后置版本j = i++,先赋值后加一

优先使用前置版本,后置多一步储存原始值。(除非需要变化前的值)

混用解引用和递增运算符

*iter++等价于*(iter++),递增优先级较高

auto iter = vi.begin();
while (iter!=vi.end()&&*iter>=0)cout<<*iter++<<endl;    // 输出当前值,指针向前移1

简介是一种美德,追求简洁能降低程序出错可能性

成员访问运算符

ptr->mem等价于(*ptr).mem

注意.运算符优先级大于*,所以记得加括号

条件运算符

  • 条件运算符(?:)允许我们把简单的if-else逻辑嵌入到单个表达式中去,按照如下形式:cond? expr1: expr2

  • 可以嵌套使用,右结合律,从右向左顺序组合

    • finalgrade = (grade > 90) ? "high pass": (grade < 60) ? "fail" : "pass";
      //等价于
      finalgrade = (grade > 90) ? "high pass": ((grade < 60) ? "fail" : "pass");
  • 输出表达式使用条件运算符记得加括号,条件运算符优先级太低。

位运算符

用于检查和设置二进制位的功能。

  • 位运算符是作用于整数类型的运算对象。

  • 二进制位向左移(<<)或者向右移(>>),移出边界外的位就被舍弃掉了。

  • 位取反(~)(逐位求反)、与(&)、或(|)、异或(^

有符号数负值可能移位后变号,所以强烈建议位运算符仅用于无符号数

应用:

unsigned long quiz1 = 0;    // 每一位代表一个学生是否通过考试
1UL << 12;  // 代表第12个学生通过
quiz1 |= (1UL << 12);   // 将第12个学生置为已通过
quiz1 &= ~(1UL << 12);  // 将第12个学生修改为未通过
bool stu12 = quiz1 & (1UL << 12);   // 判断第12个学生是否通过

位运算符使用较少,但是重载cout、cin大家都用过

位运算符满足左结合律,优先级介于中间,使用时尽量加括号。

sizeof运算符

  • 返回一条表达式或一个类型名字所占的字节数

  • 返回的类型是 size_t的常量表达式。

  • sizeof并不实际计算其运算对象的值。

  • 两种形式:

    1. sizeof (type),给出类型名

    2. sizeof expr,给出表达式

  • 可用sizeof返回数组的大小

int ia[10];
// sizeof(ia)返回整个数组所占空间的大小
// sizeof(ia)/sizeof(*ia)返回数组的大小
constexpr size_t sz = sizeof(ia)/sizeof(*ia);
int arr[sz];

逗号运算符

从左向右依次求值。

左侧求值结果丢弃,逗号运算符结果是右侧表达式的值。

类型转换

隐式类型转换

设计为尽可能避免损失精度,即转换为更精细类型。

  • int类型小的整数值先提升为较大的整数类型。

  • 条件中,非布尔转换成布尔。

  • 初始化中,初始值转换成变量的类型。

  • 算术运算或者关系运算的运算对象有多种类型,要转换成同一种类型。

  • 函数调用时也会有转换。

算术转换

整型提升

  • 常见的char、bool、short能存在int就会转换成int,否则提升为unsigned int

  • wchar_t,char16_t,char32_t提升为整型中int,long,long long ……最小的,且能容纳原类型所有可能值的类型。

其他转换

p143

显式类型转换(尽量避免)

  • static_cast:任何明确定义的类型转换,只要不包含底层const,都可以使用。 double slope = static_cast<double>(j);

  • dynamic_cast:支持运行时类型识别。

  • const_cast:只能改变运算对象的底层const,一般可用于去除const性质。 const char *pc; char *p = const_cast<char*>(pc)

    只有其可以改变常量属性

  • reinterpret_cast:通常为运算对象的位模式提供低层次上的重新解释。

旧式强制类型转换

type expr

运算符优先级表

p147

第五章 语句

简单语句

  • 表达式语句:一个表达式末尾加上分号,就变成了表达式语句。

  • 空语句:只有一个单独的分号。

  • 复合语句(块):用花括号 {}包裹起来的语句和声明的序列。一个块就是一个作用域。

条件语句

  • 悬垂else(dangling else):用来描述在嵌套的if else语句中,如果ifelse多时如何处理的问题。C++使用的方法是else匹配最近没有配对的if

迭代语句

  • while:当不确定到底要迭代多少次时,使用 while循环比较合适,比如读取输入的内容。

  • forfor语句可以省略掉 init-statementconditionexpression的任何一个;甚至全部

  • 范围forfor (declaration: expression) statement

跳转语句

  • breakbreak语句负责终止离它最近的whiledo whilefor或者switch语句,并从这些语句之后的第一条语句开始继续执行。

  • continue:终止最近的循环中的当前迭代并立即开始下一次迭代。只能在whiledo whilefor循环的内部。

try语句块和异常处理

  • throw表达式:异常检测部分使用 throw表达式来表示它遇到了无法处理的问题。我们说 throw引发 raise了异常。

  • try语句块:以 try关键词开始,以一个或多个 catch字句结束。 try语句块中的代码抛出的异常通常会被某个 catch捕获并处理。 catch子句也被称为异常处理代码

  • 异常类:用于在 throw表达式和相关的 catch子句之间传递异常的具体信息。

第六章 函数

函数基础

  • 函数定义:包括返回类型、函数名字和0个或者多个形参(parameter)组成的列表和函数体。

  • 调用运算符:调用运算符的形式是一对圆括号 (),作用于一个表达式,该表达式是函数或者指向函数的指针。

  • 圆括号内是用逗号隔开的实参(argument)列表。

  • 函数调用过程:

    • 1.主调函数(calling function)的执行被中断。

    • 2.被调函数(called function)开始执行。

  • 形参和实参:形参和实参的个数类型必须匹配上。

  • 返回类型void表示函数不返回任何值。函数的返回类型不能是数组类型或者函数类型,但可以是指向数组或者函数的指针。

  • 名字:名字的作用于是程序文本的一部分,名字在其中可见。

局部对象

  • 生命周期:对象的生命周期是程序执行过程中该对象存在的一段时间。

  • 局部变量(local variable):形参和函数体内部定义的变量统称为局部变量。它对函数而言是局部的,对函数外部而言是隐藏的。

  • 自动对象:只存在于块执行期间的对象。当块的执行结束后,它的值就变成未定义的了。

  • 局部静态对象static类型的局部变量,生命周期贯穿函数调用前后。

函数声明

  • 函数声明:函数的声明和定义唯一的区别是声明无需函数体,用一个分号替代。函数声明主要用于描述函数的接口,也称函数原型

  • 在头文件中进行函数声明:建议变量在头文件中声明;在源文件中定义。

  • 分离编译CC a.cc b.cc直接编译生成可执行文件;CC -c a.cc b.cc编译生成对象代码a.o b.oCC a.o b.o编译生成可执行文件。

参数传递

  • 形参初始化的机理和变量初始化一样。

  • 引用传递(passed by reference):又称传引用调用(called by reference),指形参是引用类型,引用形参是它对应的实参的别名。

  • 值传递(passed by value):又称传值调用(called by value),指实参的值是通过拷贝传递给形参。

传值参数

  • 当初始化一个非引用类型的变量时,初始值被拷贝给变量。

  • 函数对形参做的所有操作都不会影响实参。

  • 指针形参:常用在C中,C++建议使用引用类型的形参代替指针。

传引用参数

  • 通过使用引用形参,允许函数改变一个或多个实参的值。

  • 引用形参直接关联到绑定的对象,而非对象的副本。

  • 使用引用形参可以用于返回额外的信息

  • 经常用引用形参来避免不必要的复制。

  • void swap(int &v1, int &v2)

  • 如果无需改变引用形参的值,最好将其声明为常量引用。

const形参和实参

  • 形参的顶层const被忽略。void func(const int i);调用时既可以传入const int也可以传入int

  • 我们可以使用非常量初始化一个底层const对象,但是反过来不行。

  • 在函数中,不能改变实参的局部副本

  • 尽量使用常量引用。

数组形参

  • 当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。

  • 要注意数组的实际长度,不能越界。

main处理命令行选项

  • int main(int argc, char *argv[]){...}

  • 第一个形参代表参数的个数;第二个形参是参数C风格字符串数组。

可变形参

initializer_list提供的操作(C++11):

操作 解释
initializer_list<T> lst; 默认初始化;T类型元素的空列表
initializer_list<T> lst{a,b,c...}; lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const
lst2(lst) 拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素。
lst2 = lst 同上
lst.size() 列表中的元素数量
lst.begin() 返回指向lst中首元素的指针
lst.end() 返回指向lst中微元素下一位置的指针

initializer_list使用demo:

void err_msg(ErrCode e, initializer_list<string> il){cout << e.msg << endl;for (auto bed = il.begin(); beg != il.end(); ++ beg)cout << *beg << " ";cout << endl;
}
​
err_msg(ErrCode(0), {"functionX", "okay});
  • 所有实参类型相同,可以使用 initializer_list的标准库类型。

  • 实参类型不同,可以使用可变参数模板

  • 省略形参符: ...,便于C++访问某些C代码,这些C代码使用了 varargs的C标准功能。

返回类型和return语句

无返回值函数

没有返回值的 return语句只能用在返回类型是 void的函数中,返回 void的函数不要求非得有 return语句。

有返回值函数

  • return语句的返回值的类型必须和函数的返回类型相同,或者能够隐式地转换成函数的返回类型。

  • 值的返回:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。

  • 不要返回局部对象的引用或指针

  • 引用返回左值:函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值;其他返回类型得到右值。

  • 列表初始化返回值:函数可以返回花括号包围的值的列表。(C++11

  • 主函数main的返回值:如果结尾没有return,编译器将隐式地插入一条返回0的return语句。返回0代表执行成功。

返回数组指针

  • Type (*function (parameter_list))[dimension]

  • 使用类型别名: typedef int arrT[10]; 或者 using arrT = int[10;],然后 arrT* func() {...}

  • 使用 decltypedecltype(odd) *arrPtr(int i) {...}

  • 尾置返回类型: 在形参列表后面以一个->开始:auto func(int i) -> int(*)[10]C++11

函数重载

  • 重载:如果同一作用域内几个函数名字相同但形参列表不同,我们称之为重载(overload)函数。

  • main函数不能重载。

  • 重载和const形参

    • 一个有顶层const的形参和没有它的函数无法区分。 Record lookup(Phone* const)Record lookup(Phone*)无法区分。

    • 相反,是否有某个底层const形参可以区分。 Record lookup(Account*)Record lookup(const Account*)可以区分。

  • 重载和作用域:若在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体,在不同的作用域中无法重载函数名。

特殊用途语言特性

默认实参

  • string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');

  • 一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。

内联(inline)函数

  • 普通函数的缺点:调用函数比求解等价表达式要慢得多。

  • inline函数可以避免函数调用的开销,可以让编译器在编译时内联地展开该函数。

  • inline函数应该在头文件中定义。

constexpr函数

  • 指能用于常量表达式的函数。

  • constexpr int new_sz() {return 42;}

  • 函数的返回类型及所有形参类型都要是字面值类型。

  • constexpr函数应该在头文件中定义。

调试帮助

  • assert预处理宏(preprocessor macro):assert(expr);

开关调试状态:

CC -D NDEBUG main.c可以定义这个变量NDEBUG

void print(){#ifndef NDEBUGcerr << __func__ << "..." << endl;#endif
}

函数匹配

  • 重载函数匹配的三个步骤:1.候选函数;2.可行函数;3.寻找最佳匹配。

  • 候选函数:选定本次调用对应的重载函数集,集合中的函数称为候选函数(candidate function)。

  • 可行函数:考察本次调用提供的实参,选出可以被这组实参调用的函数,新选出的函数称为可行函数(viable function)。

  • 寻找最佳匹配:基本思想:实参类型和形参类型越接近,它们匹配地越好。

函数指针

  • 函数指针:是指向函数的指针。

  • bool (*pf)(const string &, const string &); 注:两端的括号不可少。

  • 函数指针形参

    • 形参中使用函数定义或者函数指针定义效果一样。

    • 使用类型别名或者decltype

  • 返回指向函数的指针:1.类型别名;2.尾置返回类型。

CppPrimer 学习笔记(1)相关推荐

  1. CppPrimer学习笔记(2)

    目录 第七章 类 (Class) 定义抽象数据类型 类成员 (Member) 类的成员函数 非成员函数 类的构造函数 访问控制与封装 友元 封装的益处 类的其他特性 类的作用域 构造函数再探 委托构造 ...

  2. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  3. 容器云原生DevOps学习笔记——第三期:从零搭建CI/CD系统标准化交付流程

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  4. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  5. 2020年Yann Lecun深度学习笔记(下)

    2020年Yann Lecun深度学习笔记(下)

  6. 2020年Yann Lecun深度学习笔记(上)

    2020年Yann Lecun深度学习笔记(上)

  7. 知识图谱学习笔记(1)

    知识图谱学习笔记第一部分,包含RDF介绍,以及Jena RDF API使用 知识图谱的基石:RDF RDF(Resource Description Framework),即资源描述框架,其本质是一个 ...

  8. 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记

    计算机文化基础(第十讲)学习笔记 采样和量化PictureElement Pixel(像素)(链接: 采样的实质就是要用多少点(这个点我们叫像素)来描述一张图像,比如,一幅420x570的图像,就表示 ...

  9. Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)

    Go by example 中文版 Go 构建 Web 应用 Go 学习笔记:无痕 Go 标准库中文文档 Golang开发新手常犯的50个错误 50 Shades of Go: Traps, Gotc ...

最新文章

  1. 2022-2028年中国水性密封胶行业市场调查研究及未来趋势预测报告
  2. delphi 登录界面 主窗体 切换_winform项目——仿QQ即时通讯程序06:主界面交互逻辑...
  3. boost::mp11::mp_any_of相关用法的测试程序
  4. js为链接绑定点击事件并且附带return false;来阻止跳转
  5. PostgreSQL在Update时使用Substring函数截取字符串并且加上CASE WHEN THEN条件判断
  6. 英特尔携手ATT和爱立信进行DIRECTV NOW流媒体直播服务的5G试验
  7. 21位美国名校学生领袖在湖北了解中国媒体情况
  8. [Web Chart系列之二] 各种实现js 图表的library汇总与比较
  9. apollo编译源码使用并将eureka替换为自己的eureka服务
  10. 《程序是怎样跑起来的》读书笔记——第三章 计算机进行小数运算时出错的原因...
  11. 流媒体服务器搭建详解
  12. MySQL-快速入门(13)MySQL日志
  13. 基于 Spark推荐 系统应用 现 状
  14. 从入门到精通,C程序员必读的3本
  15. vue生成txt文件下载
  16. 基于树莓派的语音邮件收发
  17. ADSL防御黑客进攻的方法
  18. android缓存bilibili,bilibili缓存姬
  19. linux中安装搜狗拼音输入法
  20. 手机如何访问电脑局域网文件共享服务器,手机怎么访问局域网电脑共享文件

热门文章

  1. 【众说区块链】公链是否一定要发币,Token到底应该怎么理解?
  2. 你真的认为自己熟练Python?带你一篇文章 查漏补缺,感受自己离深入掌握 Python 还有多远。
  3. linux编译.o文件,使用-O0编译Linux内核
  4. 【车辆计数】基于光流法实现车辆检测计数matlab 源码
  5. CAN总线协议报文浅析
  6. 怎么用python抓取网页数据
  7. 服务器光信号灯亮红灯,路由器光信号闪红灯是怎么回事
  8. java——菜鸟飞机大战
  9. 中心极限定理与大数定理理解
  10. HTML学生个人网站作业设计:动漫网站设计——斗破苍穹动漫(6页) HTML+CSS+JavaScript 简单DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载