文章目录
前言
本篇文章笔者将会对 CPP (基础入门) 部分进行详细讲解 , 以最易懂的方式介绍给学者 , 小白也能懂 , 相信通过本篇章学者将会对 CPP( C++) 入门更加容易 !
一、什么 C++ 语言 ?
● C++是一种计算机高级程序设计语言,由C语言扩展升级而产生。C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。
● C++是面向对象语言的一种 , 在 C 语言的基础上优化改进 , 让程序员运用起来更加方便.
二、C++ 的重要性
● 数据展示: 笔者以 TIOBE 编程语言排行榜的数据为例 . 以下是 TIOBE 7 月 最新编程语言排行榜统计数据 :
C++ 还是占有很不错的位置的 , 好多人都认为 C++ 难就不重要 , 这是非常错误的一个想法 .
● 用途: 提到 C++ 很多人就会立马想到游戏 , 其实 C++ 还是有很多方面的用途 .
C++的应用领域服务器端、游戏(引擎)、机器学习引擎、⾳视频处理、嵌⼊式软件、电信设备、⾦融
应⽤、基础库、操作系统、编译器、基础架构、基础⼯具、硬件交互等很多方面 .
三、C++ 入门基础
想要入门一门语言 , 那么就必须从语言的基础语法部分开始了解 .
◇ 第一个 C++ 程序
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World !\n" << endl;
return 0;
}
这里笔者将会为大家一 一 解释第一个 C++ 程序 .
● 对比第一个 C 语言程序 .
#include <stdio.h>
int main()
{
printf("Hello World !\n ");
return 0;
}
命名空间
这是 C++ 对 C 的一个改进 , C 语言存在一个作用域无法使用同一名字的缺陷 . 那么想要使用同一名字来定义便不可行了. 所以 C++ 中提出了 namespace 的概念 .
给以下 2 个程序
●
#include <stdio.h>
//#include <stdlib.h>
int rand = 10; // 全局变量
int main()
{
printf("%d\n" , rand);
return 0;
}
●
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
printf("%d\n" , rand);
return 0;
}
运行以上 2 个程序, 会发现 第 2 个程序 出现了报错 .
这里提示 ’ rand ’ 函数重定义 , 为什么呢?
● 首先要了解一下 C 语言中 rand 函数 (生成随机数) 是包含在 库 <stdlib.h> 中的 . (笔者之前扫雷有过讲解 , 可前往自行学习) !
● 变量 rand 为全局变量 , 库 <stdlib.h> 也是作用在全局之中 . 这就与 C 语言中规定相违背了 , 于是引出 " namespace " 命名空间这样的概念 .
※ namespace 定义 :
● 定义命名空间,需要使用到 namespace 关键字,后面跟命名空间的名字,然后接⼀对 " {} " 即可,{} 中即为: 命名空间的成员。命名空间中可以定义 变量/ 函数/ 类型等。
● namespace 本质 是 定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量 .
● C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量 / 函数 / 类型出处(声明或定义)的逻辑,所以有了域的隔离,名字冲突就解决了。
● 局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
※ namespace 只能 定义在全局 , 它也可以嵌套定义 , 以下给出例子:
#include <iostream>
using namespace std;
// 嵌套定义 namespace
namespace AGE
{
namespace GJG
{
int age = 20;
}
namespace LiSi
{
int age = 24;
}
}
int main()
{
cout << "AGE::GJG::age: " << AGE::GJG::age << endl;
cout << "AGE::LiSi::age: " << AGE::LiSi::age << endl;
return 0;
}
※ 项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。
■ 会默认合并到一起(逻辑上) , 向同一个 namespace 一样 . 以下给出例子:
定义一个栈
Stack.h
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
//项目中多个文件协同时, 为了避免出现同一作用域同名的情况,使用命名空间解决
namespace GJG_Stack
{
typedef int STDataType;
typedef struct Stack
{
int top;
int capacity;
}ST;
void STInit(ST* ps, int n);
void STDestroy(ST* ps);
void STPush(ST* ps, STDataType x);
void STPop(ST* ps);
STDataType STTop(ST* ps);
int STSize(ST* ps);
bool STEmpty(ST* ps);
}
Stack.cpp
#include "Stack.h"
namespace GJG_Stack
{
void STInit(ST* ps, int n)
{
}
void STDestroy(ST* ps)
{
}
void STPush(ST* ps, STDataType x)
{
}
void STPop(ST* ps)
{
}
STDataType STTop(ST* ps)
{
}
int STSize(ST* ps)
{
}
bool STEmpty(ST* ps)
{
}
}
※ C++标准库都放在⼀个叫 std(standard) 的命名空间中。
所以在第一个 C++ 程序中 std:: 就表示限定了在 std 这个标准库中查找
※ 用 namespace 对 C 语言程序改进
● 补充 : " :: " , 本符号为作用域限定符号 . 限定函数查找的作用域.
#include <stdio.h>
#include <stdlib.h>
namespace GJG
{
int rand = 10;
}
int main()
{
printf("%d\n" , GJG ::rand);
return 0;
}
可以看到很顺利的运行了 !
命名空间的使用
●
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。
#include <stdio.h>
namespace GJG
{
int a = 5;
}
//int a = 10;
int main()
{
printf("%d\n", a); // 编译报错未声明标识符 a
return 0;
}
所以我们要使用命名空间中定义的变量/函数,有三种方式:
▶指定命名空间展开
#include <stdio.h>
namespace GJG
{
int a = 5;
}
//int a = 10;
int main()
{
printf("%d\n", GJG::a); // 指定 在 GJG 域中查找
return 0;
}
▶使用 using 全部展开
C语言版本
#include <stdio.h>
namespace GJG
{
int a = 5;
int b = 4;
int c = 3;
}
using namespace GJG;
int main()
{
printf(" a = %d\n", a);
printf(" b = %d\n", b);
printf(" c = %d\n", c);
return 0;
}
C++ 版本
#include <iostream>
namespace GJG
{
int a = 5;
int b = 4;
int c = 3;
}
using namespace GJG;
using namespace std;
int main()
{
//未展开 std 域时
std::cout << "GJG :: a = " << GJG::a << std::endl;
std::cout << "GJG :: b = " << GJG::b << std::endl;
std::cout << "GJG :: c = " << GJG::c << std::endl <<endl;
//展开 std 域时
cout << "GJG :: a = " << GJG::a << endl;
cout << "GJG :: b = " << GJG::b << endl;
cout << "GJG :: c = " << GJG::c << endl;
return 0;
}
▶使用 using 部分展开
● 意思是只展开常用的
C语言版本
#include <iostream>
namespace GJG
{
int a = 5;
int b = 4;
int c = 3;
}
using GJG::a; // 只展开常用的 a
int main()
{
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", GJG ::c);
return 0;
}
■ 补充: 这里发现没有包含头文件 <stdio.h> 也能执行 printf , 原因是 : C++ 兼容 大部分C 语言 .
C++ 版本
#include <iostream>
namespace GJG
{
int a = 5;
int b = 4;
int c = 3;
}
using namespace std;
using GJG::b;
int main()
{
cout << "GJG:: b = " << b << endl;
cout << "GJG:: b = " << b << endl;
cout << "GJG:: b = " << b << endl;
cout << "GJG:: b = " << b << endl;
cout << "GJG:: b = " << b << endl << endl;
cout << "GJG:: c = " << GJG::c << endl;
return 0;
}
◇ C++ 的 输入 和 输出
● iostream 是 Input Output Stream的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
● std::cin是istream类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。 其中 C 指 : characters (字符).
● std 是标准库 , cin , cout 是在标准库中.
● std::cout是 iostream 类的对象,它主要面向窄字符的标准输出流.
● cout 点击查看官网解释 - 将代码中的变量转化为字符输出到终端控制台上
● std::endl (end line )是⼀个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区 .
● << 是流插入运算符,对应 cout , >>是流提取运算符 , 对应 cin .
※ cout /cin /endl等 都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们. 就要用到域限定符号 .
※ C++ 中能自动识别变量的类型
#include <iostream>
using namespace std;
int main()
{
int a = 0;
double b = 2.2;
char c = 'c';
cout << a << " " << b << " " << c << endl;
return 0;
}
※ 输入 与 输出 代码
#include <iostream>
using namespace std;
int main()
{
int a = 0;
double b = 0.0;
char c = '0';
cin >> a >> b >> c; // 插入
cout << a << " " << b << " " << c << endl;
return 0;
◇ 缺省参数
缺省参数也是 C++ 中比较重要的内容 , 这个可以为我们函数传参带来很大方便.
● 什么是缺省参数? : 缺省参数就是 : 声明或定义函数时为函数的参数指定⼀个缺省值。
※ 如果函数在传参时实参部分传了值 , 那么形参部分就使用实参部分传的值.
※ 如果函数在传参时实参部分未传值 , 那么形参部分就会使用缺省值.
◑ 代码展示:
#include <iostream>
using namespace std;
// 缺省参数
//全缺省
void Func1(int x = 1 , int y = 2 , int z = 3)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
cout << endl;
cout << endl;
}
int main()
{
Func1(); //全部使用缺省值
Func1(4);
Func1(4, 5);
Func1(4,5,6); // 全部使用实参值
return 0;
}
缺省值分为两种类型 :
● 全缺省
※ 形参部分全部都给了缺省值.
#include <iostream>
using namespace std;
// 缺省参数
//全缺省
void Func1(int x = 1 , int y = 2 , int z = 3)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
cout << endl;
cout << endl;
}
int main()
{
Func1();
Func1(4);
Func1(4, 5);
Func1(4,5,6);
return 0;
}
● 半缺省
※ 形参部分一部分给了缺省值.
#include <iostream>
using namespace std;
//半缺省
void func(int x , int y = 1 , int z = 2)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
cout << endl;
cout << endl;
}
int main()
{
func(3);
func(3, 4);
func(3, 4, 5);
return 0;
}
◑ 特别注意:
● 半缺省 的缺省参数只能从右往左依次连续缺省 , 不能跳跃缺省 .
● 带缺省参数的函数调用时 , 实参部分必须从左到右依次传参 , 不能跳跃传参.
※ C++ 规定 : 函数声明和定义分离时多个(多个文件时),缺省参数不能在函数声明和定义中同时出现 , 必须在函数声明中给缺省值 .
以下给出例子: 实现栈
声明文件和定义文件中同时给缺省参数 , 错误示范!!!
Stack.h
#include <iostream>
#include <assert.h>
using namespace std;
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST& ps, int n = 4); // 半缺省
Stack.cpp
#include"Stack.h"
// 缺省参数不能声明和定义同时给
void STInit(ST& ps, int n = 4) // 此处为错误示范!!!!!! 应为 - ( int n )
{
ps.a = (STDataType*)malloc(n * sizeof(STDataType));
ps.top = 0;
ps.capacity = n;
}
Test.cpp
#include"Stack.h"
int main()
{
ST s1;
STInit(s1);
// 确定知道要插⼊500个数据,初始化时⼀把开好,避免扩容
ST s2;
STInit(s2, 500);
return 0;
}
出现报错 : “STInit”: 重定义默认参数 : 参数 1
※ 故: 只能在 .h 文件中给缺省值 .
◇ 函数重载
C++中允许同一作用域中出现同名函数 , 但有两个条件,这也是构成重载的条件:
● 函数参数的类型不同
● 函数参数的个数不同
◑ 代码展示:
● 函数参数的类型不同
#include <iostream>
using namespace std;
//函数重载
//函数参数类型不同
int Add(int x, int y)
{
return x + y;
}
double Add(double a , double b)
{
return a + b;
}
int main()
{
cout << "两个整数相加: " << Add(1, 3) << endl;
cout << endl;
cout << "两个浮点数相加: " << Add(2.2, 3.0) << endl;
return 0;
}
● 函数参数的个数不同
#include <iostream>
using namespace std;
//函数重载
//函数参数个数不同
int func(int a , int b)
{
int c = a * b;
return c;
}
int func(int a)
{
int c = a * 1;
return c;
}
int main()
{
cout << "func(int a , int b) : " << func(2,3) << endl;
cout << endl;
cout << "func(int a) :" << func(6) << endl;
return 0;
}
◑ 补充: 参数类型顺序不同也构成重载 – 实质上属于参数类型不同的一种.
//3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
◑ 特例: 以下两个函数构成重载 ,但在调用时会有歧义 ,编译器会报错
#include <iostream>
using namespace std;
void func()
{
cout << "func()" << endl;
}
void func(int a = 1) // 全省参数函数
{
cout << "func(int a)" << endl;
}
int main()
{
func();
return 0;
}
分析: 全省参数函数在调用时可以不传实参 , 然而 两个 func 函数都会存在不传参的问题, 编译器就不知道调用谁了 , 就会出现报错.
◇ 引用
引用是 C++ 中非常重要部分 , 在实际用途中也起着很大的作用 .
◑ 概念: 可以这样理解 : 引用其实就是起别名 , 它并没有开辟空间 , 起了别名就完全可以替代原来的变量. 它和它引用的变量共用同⼀块内存空间。 但其底层是 : 指针.
● 表面上是传值 , 但底层是指针 , 不能片面的说引用就是传值.
◑ 符号: ’ & ’ 为引用符号 , 注意: C 语言中该符号为 : 取地址.
● 放在类型后为引用
● 放在变量前为取地址
◑ 代码展示:
#include <iostream>
using namespace std;
//引用
int main()
{
int a = 2;
int& b = a; // b 是 a 的别名
int& c = b; // c 是 b 的别名
c++;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << endl;
cout << endl;
cout << " &a = " << &a << endl;
cout << " &b = " << &b << endl;
cout << " &c = " << &c << endl;
//运行后会发现三者地址完全相同 .
return 0;
}
◑ 引用的特性:
● 引用在定义时必须初始化
● 一个变量可以有多个引用 , 也可以对已经引用的变量进行引用
● 引用一旦引用了一个实体 , 就不能再引用其它实体了
#include <iostream>
using namespace std;
int main()
{
int a = 2;
int& b = a;
int c = 3;
int& b = c;
//会报错 b 多次初始化
}
◑ 引用的使用:
● 引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。
举个例子 , 交换两个变量的值 . 用引用就会很方便
#include <iostream>
using namespace std;
//交换两个变量的值
void Swap(int& pa , int& pb)
{
int tmp = pa;
pa = pb;
pb = tmp;
}
int main()
{
int a = 2;
int b = 3;
cout << "交换前: a = " << a << " " << "b = " << b << endl;
cout << endl;
Swap(a, b);
cout << "交换后: a = " << a << " " << "b = " << b << endl;
return 0;
}
● 引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引用(如Java)是有很大的区别的,除了用法,最大的点,C++引用定义后不能改变指向,Java的引用可以改变指向。
◑ const 引用:
● 可以引用一个 const 对象 , 但必须用 const 引用.
● 可以引用一个普通对象.(普通,产生临时变量的都可以)
▶引用一个const 对象
#include <iostream>
using namespace std;
//引用一个 const 对象
int main()
{
const int a = 10; // 修饰整体 -- 只可读 , 不可写入(不可改变a的值)
//int& b = a; // 放大了b 的权限, 是错误的
const int& b = a; //与a拥有一样的权限 , 只可读 , 不可写
return 0;
}
▶引用一个普通对象
#include <iostream>
using namespace std;
//引用一个 普通 对象
int main()
{
int a = 20;
int& c = a; // ok
//引用对 b 权限的缩小
const int& b = a; // ok
return 0;
}
▶特别注意: 三个会产生临时变量的情况:
◑: 临时变量是具有常性的 , 不能被修改!
◑: 传值返回
◑: 表达式运算
◑: 类型转换
▶传值返回
#include <iostream>
using namespace std;
//传值返回 - 产生临时变量具有常性
int fun(int a)
{
return a;
}
int main()
{
//int& ret = fun(1); // err
const int& ret = fun(1); // ok
// fun函数返回时先把值给了临时变量, 然后赋值给ret
return 0;
}
▶表达式运算
#include <iostream>
using namespace std;
//表达式运算 - 产生临时变量具有常性
int main()
{
int a = 1;
int b = 2;
//int& c = a + b; // err
const int& c = a + b;
return 0;
}
▶类型转换
int main()
{
int a = 1;
double b = a;
// int& rb = b; //“初始化”: 无法从“double”转换为“int &”
const int& rb = b;
return 0;
}
总结
以上是笔者对于基础部分的大致讲解 , 内容同时也是笔者对于学习的总结 , 有不足的地方望指出! 喜欢笔者,持续关注哦!