👾1.1 如何撰写一个C++程序

c++程序一般从一个 main() 函数开始;
例如

  1. #include<iostream> //头文件,包含class的声明
  2. #include<string>
  3. using namespace std; //命名空间
  4. int main() //int:整形数据类型
  5. { //函数用{……}来表示其范围
  6. string user_name; //定义了一个对象
  7. cout<<"Please enter your name";
  8. cin>>user_name;
  9. cout<<"Hello,"
  10. <<user_name
  11. <<"and goodbye";
  12. return 0; //函数应有返回值;
  13. }

函数

函数是一块独立的程序代码序列(code sequence),能够执行一些运算;包含四个部分:

  • 返回值类型(return type)
  • 函数名称;
  • 参数列表(parameter list)
  • 函数体

main() 并非程序语言中的关键字,执行程序时会假定已经定义了 main()

👾类class

定义一个类,本质上是定义一个数据类型的蓝图。这实际上并没有定义任何数据,但它定义了类的名称意味着什么,也就是说,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作

所谓类(class),是用户自定义的数据类型。
class机制赋予了我们增加程序内之类型抽象化层次的能力;
class的定义一般分两部分,分别写在不同的文件中。

  • 头文件(header file):声明该class所提供的各种操作行为(operation)
  • 程序代码文件(program text):包含这些操作行为的实现内容(implementation)

命名空间

所谓命名空间(namespace)是一种将库名称封装起来的方法。通过这种方法可以避免和应用程序发生命名冲突的问题。

假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。 同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。 因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。 我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。

小结

  • C++程序主要由函数、运算符、对象组成
  • 对象可以是常量,也可以是变量
  • 类的机制使得我们可以定义自己需要的对象

🦋1.2 对象的定义与初始化

为了定义对象,需要的:

  • 命名
  • 赋予数据类型

    ✨命名规则

    :::info C++ 标识符是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。习惯**将类内的成员变量以下划线开头
    C++ 标识符内不允许出现标点字符,比如 @、& 和 %。C++ 是区分大小写的编程语言。因此,在 C++ 中,
    Manpowermanpower** 是两个不同的标识符。 ::: 且不允许为已经定义的系统关键字
    单一语句中可以一并定义多个对象,以“,”分隔,也可以:int num_1=0, num_2=1;

初始化方法

用assignment运算符=进行初始化 string seqence_name="Fibonacci";
构造函数语法: int num_tries(0);

*内置数据类型

内置的基本数据类型

类型 关键字
布尔型 bool
字符型 char
整型 int
浮点型 float
双浮点型 double
无类型 void
宽字符型 wchar_t

其实 wchar_t 是这样来的: typedef short int wchar_t 所以 wchar_t 实际上的空间是和 short int 一样。
一些基本类型可以使用一个或多个类型修饰符进行修饰:

  • signed
  • unsigned
  • short
  • long

**

const关键字

声明为const的变量在程序执行过程中不应该有变动,在获得初值后无法再有任何变动;如果企图改变const对象,会产生编译错误。
**

小结

  • 对象的命名必须按照一定的规则
  • 对象初始化的方式有两种,assignment初始化和构造函数初始化
  • const定义的常量必须在定义时初始化

    1.3 撰写表达式

    本节中主要介绍了运算符,C++之中的运算符可以分成四种类型:

  • 算术运算符

  • 关系运算符
  • 逻辑运算符
  • 复合赋值运算符

    算术运算符

    算数运算符基本没有什么需要注意的,唯一需要注意的是除法和求余;对于整数进行除法时,小数点后面的部分会被舍弃。
运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 30
- 从第一个操作数中减去第二个操作数 A - B 将得到 -10
* 把两个操作数相乘 A * B 将得到 200
/ 分子除以分母 B / A 将得到 2
% 取模运算符,整除后的余数 B % A 将得到 0
++ 自增运算符,整数值增加 1 A++ 将得到 11
自减运算符,整数值减少 1 A— 将得到 9

关系运算符

运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 不为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 不为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 不为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真

逻辑运算符

运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都 true,则条件为 true。 (A && B) 为 false。
|| 称为逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。 (A || B) 为 true。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态,如果条件为 true 则逻辑非运算符将使其为 false。 !(A && B) 为 true。

位运算符

运算符 描述 实例
& 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 (A & B) 将得到 12,即为 0000 1100
| 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 (A | B) 将得到 61,即为 0011 1101
^ 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 (A ^ B) 将得到 49,即为 0011 0001
~ 二进制补码运算符是一元运算符,具有”翻转”位效果,即0变成1,1变成0。 (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<< 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 A << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 A >> 2 将得到 15,即为 0000 1111

注意补码运算与逻辑非运算不是一个东西

杂项运算符

运算符 描述
sizeof sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
, 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.(点)和 ->(箭头) 成员运算符用于引用类、结构和共用体的成员。
Cast 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
& 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
* 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

来源于菜鸟教程

1.4 条件语句和循环语句

本小结介绍了基本的几个条件语句和循环语句

  • if-else
  • while
  • switch

不多加描述了

1.5 如何运用Array和Vector

array和vector是两种存放连续数值的容器类型,这两种容器可以按地址作为索引来调用容器中的数据。
数组为C++内置的数据类型,而vector是一个class template;
数组的声明一般如: typename arrayname[arraysize];

array的初始化

array的初始话有多种方式:

  • 可以在定义时初始化: int elem_seq[]={1,2,3}
  • 也可以定义空数组然后依次初始化每个元素

Vector的初始化与array不相同,在class template中会详细解释;

值得注意的一点是数组名和下标实际上是代表的内存地址

小结

  • array和vector都是存放数据的容器,既可以通过名称,又可以通过存放数据的位置来读写数据
  • array大小必须是常量表达式
  • 若初始化时不赋值,则数组中的值是之前该块内存中所存储的值

    🐧1.6 指针带来的弹性

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。我们可以操作指针,而不再直接操作对象。
通常指针具有以下形式:
type_of_object_pointed_to * name_of_pointer_object
e.g.
vector<int> *pv=0;

  • 在定义某特定类型的指针时,需要在类型名后加上 *
  • 如果希望取得对象所在的内存地址则应使用取地址运算符 &
  • 如要访问一个指针所指的对象,需要对该指针进行提领(dereference)操作,即 *pv

指针的复杂往往来源于以下几点

  • 指针具有双重性质:既可以操作指针包含的内存地址,又可以操作指针所指的对象值;
  • 指针可能不指向任何对象,在提领时可能会(也可能不会)出现错误; :::tips 对于此问题可以在初始化时统一定义指针地址为0(null指针),之后再使用指针前检验; :::
    1. int *p=0;
    2. if(p && *p!=1024)
    3. *p=1024;

    🤷*指针数组和指向数组的指针

    指针数组就是一个数组存放的都是指针变量(也就是存放的是地址)

e.g.

  1. const int seq_cnt = 6;
  2. // 一个指针数组,大小为seq_cnt
  3. // 每个指针都指向vector<int>对象
  4. vector<int> *seq_addrs[ seq_cnt ]={
  5. &fibnacci, &lucas, &pell,
  6. &triangular, &square, &pentagoal
  7. };

seq_addrs是一个在栈区,有6个元素的数组,而每一个元素又是一个指针,所以说它的6个元素各占四个字节

数组指针
例如定义了这样一个数组指针char (*pa)[4]; ;pa是一个指针指向一个 char [4] 的数组,每个数组元素是一个char类型的变量。既然pa是一个指针,存放一个数组的地址,那么在我们定义一个数组时, char a[4]; 数组名称就是这个数组的首地址,那么这二者有什么区别和联系呢?

a是一个长度为4的字符数组,a是这个数组的首元素首地址。既然a是地址,pa是指向数组的指针,那么能将a赋值给pa吗? :::tips 答案是不行的!因为a是数组首元素首地址,pa存放的却是数组首地址,a是char 类型,a+1,a的值会实实在在的加1,而pa是char[4]类型的,pa+1,pa则会加4,虽然数组的首地址和首元素首地址的值相同,但是两者操作不同,所以类型不匹配不能直接赋值,但是可以这样:pa = &a,pa相当与二维数组的行指针,现在它指向a[4]的地址。 ::: reference

1.7 文件的读写