简介
C++ 是一种中级语言,介于底层汇编与高级语言之间,相较于高级语言更具有效率与扩展性。它是借鉴多种编程语言基于 C 的升级版本,最初的的定名为 C with Classes ,所以一个合法的 C 程序同样也是合法的 C++ 程序。
C++ 是支持面向对象编程,具有可封装、可抽象、可继承、多态的特性。
标准 C++ 由三个部分组成:
- 核心部分,包括基础的运行,变量、数据类型等。
- C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。
C++ 是编译式的语言,在完成程序后需要编译器将源代码编译为可执行的二进制程序,通常可以使用免费的 GNU 的 gcc 编译 C 或 C++,g++ 则是将 gcc 的默认语言设置为 C ++ 的特殊版本。例如:
g++ Test.cpp // 利用 g++ 对Test程序进行编译
./a.out // 输出名为 a.out 的可执行文件
Hello World // 输出内容
命名空间
C++ 的命名空间(namespace)的作用是为了避免软件模块中的变量与函数冲突。包括了定义与使用两步:
首先是定义,其中可以包含有变量、函数、类等。
namespace name{
// variables、functions、classes
}
其次是引用,包含 using 声明与新的符号 :: 称为域解析操作符,用于指明使用的空间。
using A::fp // using 声明后的本程序中如果出现了未指明命名空间的 fp,就使用 A::fp
fp = function(a, b)
B::fp = function(a, b)
头文件与 std 命名空间
C++ 的头文件系统借鉴了 C ,也同样支持 C 的各类 .h 文件。后来 C++ 引入的命名空间概念很好的避免了变量与函数的冲突,所以官方重新设计了全新的头文件系统,重新编写库,将类、函数、宏等都统一纳入一个命名空间,这个命名空间的名字就是 std 。std 是 standard 的缩写,意思是“标准命名空间”。
为了避免两者重名,新版 C++ 库也对头文件的命名做了调整,去掉了后缀 .h 。而对于原来C语言的头文件,添加一个 c 字母。(但不是绝对的,比如#include <cstdio>
,既可以在 std 中,也可以是在全局中调用)
数据与变量
数据类型
C++ 提供丰富的数据类型:无类型(void)、布尔型(bool)、字符型(char)、整形(int)、浮点型(float)、双浮点型(double)、宽字符型(wchar_t)。
一些数据类型可以使用一个或多个类型修饰符进行修饰,以扩展使用范围:有符号(signed:默认)、无符号(unsigned)、减半(short)、加倍(long)。
不同的数据类型与修饰符的组合,定义了该数据类型的位数与符号,当然在不同的操作系统和系统位数中也有不同。
有时候为了标识方便,我们还可以用关键词 typedef 将数据类型重命名,例如,我们将 int 重命名为 A ,并用 A 定义一个变量为 num,则 num 的数据类型也是 int 型。
typedef int A;
A num;
枚举类型(enumeration)是 C++ 中的一种派生数据类型,是由用户定义的若干枚举常量的集合。例如:
enum color{ red=1,green,blue } c; // 定义一个枚举数据类型,并创建变量 c。默认第一个编号为0,后面的顺延
c = blue; // 给变量 c 赋值
if(c == blue) // 这两种表达是相同的
if(c == 3)
变量
变量是一个标记调用内存中的值,而变量名就是这个标记的名称。每个变量都要有指定的类型,决定了变量存储的大小和布局,运算符可应用于变量上。
变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。即告诉编译器采用什么样的数据类型,而不同地方定义变量则有不同的使用范围,一般范围三类:
- 在函数或一个代码块内部声明的变量,称为局部变量。
- 在函数参数的定义中声明的变量,称为形式参数。
- 在所有函数外部声明的变量,称为全局变量。
变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。常见的有 static 和 extern 两种声明方式。
static 存储类,指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。
void func( void )
{
static int i = 5; // 声明局部静态变量,在调用func的过程中 i 会不断累加
i++;
}
extern 存储类用于提供一个全局变量或函数的引用,全局变量或函数对所有的程序文件都是可见的。
#include <iostream> // 文件1
int count ;
extern void write_extern(); // 调用文件2的函数
int main()
{
count = 5;
write_extern();
}
#include <iostream> // 文件2
extern int count; // 声明使用文件1的count变量
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
常量
常量为一个固定值,在程序执行的过程中不会改变,也没法被改变。
常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。
常量的定义(一般是全部大写),可以有两种形式,#define
和 const
#define 预处理器,不会被分配内存,直接存储于程序的代码段中,在预处理阶段展开,不能对宏定义进行调试。
#define LENGTH 10;
const 关键字,需要进行内存分配,存储于程序的栈区或者静态存储区中,是一个"运行时"概念,在程序运行使用,类似于一个只读行数据。
const int LENGTH = 10;
函数
习惯上,我们会将一段执行指定任务的代码封装为一个函数,便于重复调用与维护,如函数只是一两句,可调用 Lambda 进行定义调用 。函数的创建有两步,包括函数声明、函数定义。
函数声明告诉编译器函数的名称、返回类型和参数,一般放在程序的前部:
return_type function_name( parameter list );
// 例如一下,别忘了有个结束符。
int max(int num1, int num2);
函数定义则是描述了具体的执行代码:
return_type function_name( parameter list )
{
body of the function
}
在函数定义中形参有以下三种传入方式:传值调用、指针调用、引用调用,而第一种会产生内容复制。
- 传值调用:把参数的实际值赋值给函数的形式参数,修改函数内的形式参数对实际参数没有影响。
void func(int a, int b){}
- 指针调用:把参数的地址赋值给形式参数,修改形式参数会影响实际参数。
void func(int *a, int *b){}
- 引用调用:把参数的引用赋值给形式参数,修改形式参数会影响实际参数,省略了指针的调用流程,直接实现了地址的传递
void func(int &a, int &b){}
Lambda 匿名函数与表达式具体形式如下,根据具体场景也可以省去几个成分来声明“不完整”的Lambda表达式:
[capture list] (params list) mutable exception-> return type { function body }
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
- function body:函数体
[](int x, int y){ return x < y ; }
[](int x, int y) -> int { int z = x + y; return z + x; }
int a = 123; // 外部变量a
auto f = [a] { cout << a << endl; }; // 对外部a变量进行捕获输出
数组
数组是储存在一个大小固定的相同类型元素的连续的内存位置中。
一维数组的声明与初始化:
type arrayName [ arraySize ];
double balance[10]; // 包含 10 个double 的数组 balance
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0}; // 数组的大小则为初始化时元素的个数
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
多维数组的声明与初始化:
type name[size1][size2]...[sizeN];
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
访问数组:
double salary = balance[9];
int val = a[2][3];
数组中的指针,其中 balance 就是一个指向 balance[0] 的指针,因此可以在某些情况下直接传递该指针更有效率
balnace[1] = *(balance + 1) // 这是相等的
指针
每一个变量都有一个内存地址,也即是该变量在内存中的位置,可以通过连字符 & 访问获取。
指针是一个变量,其值为十六进制数是另一个变量的内存地址,type是指向的变量的类型。
type *var-name;
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
ip = &var; // 在指针变量中存储 var 的地址
cout << *ip << endl; // 访问指针中地址的值
在指针变量声明时,可以赋予 NULL 值,称为空指针,NULL 为系统定义的 0 常量。
int *ptr = NULL; // 定义一个空指针
if(ptr) // 可以判断指针是否为空
指针也能够进行算术运算,在进行算术运算时,并不是地址上的单纯加1,而是相对于数据类型字节大小的相加。假设目前在连续的 int 型(4个字节)的内存空间中:
cout << ptr << endl; // 0xbfa088b0
ptr++;
cout << ptr << endl; // 0xbfa088b4
指针变量的地址还可以被指针保存,即指向指针的指针(多级间接寻址)。
int var;
int *ptr;
int **pptr;
var = 3000;
ptr = &var; // 获取 var 的地址
pptr = &ptr; // 使用运算符 & 获取 ptr 的地址
cout << "var 值为 :" << var << endl; // 它们具有相同的输出
cout << "*ptr 值为:" << *ptr << endl;
cout << "**pptr 值为:" << **pptr << endl;
引用
引用是对该变量的一个别名,它们有共同内存地址,一旦开始引用初始化为一个对象,就不能改变。
int i=5;
int& r = i; // 声明引用变量
cout << "Value of i : " << i << endl; // 具有相同的输出
cout << "Value of i reference : " << r << endl;
在函数形参的传递中利用引用调用,把参数的引用赋值给形式参数,省略了指针的调用流程,避免了参数复制。
void swap(int& x, int& y)
{
int temp;
temp = x; // 保存地址 x 的值
x = y; // 把 y 赋值给 x
y = temp; // 把 x 赋值给 y
return;
}
结构体
结构体是 一种用户自定义的数据类型,允许存储不同类型的数据项。
结构体的定义用 struct 关键词,其中 type_name 是结构体类型的名称,member_type1 member_name1 是标准的变量定义, object_names 为声明的结构体变量。
struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
} object_names;
访问结构体的成员,使用成员访问运算符(.),成员访问运算符是结构变量名称和要访问的结构成员之间的一个句号。
struct Books
{
char title[50];
int book_id;
};Book1
strcpy( Book1.title, "C++ 教程");
在作为函数的形参传递时,方式与其他类型的变量或指针类似。
void printname( struct type_name object_names )
指向结构的指针,方式与定义指向其他类型变量的指针相似,须使用 -> 运算符进行访问。
struct Books *struct_pointer;
struct_pointer = &Book1;
struct_pointer->title;
评论区