C++基础
一、课程介绍
1. c++介绍
C++ 由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的,由于C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言 ,所以最初命名为带类的C 。C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。C++ 也是目前嵌入式 ( 无人驾驶 ) 的首选语言。
2. python 和 c++对比
程序有两种执行方式,解释执行和编译执行。
python是一种解释语言,不需要经过编译,是一边解释一边执行,由于底层封装了很多库供程序员使用,所以开发起来方便快捷,且能够很好地跨平台,写一些小工具小程序特别合适。
c++ 则属于编译语言,是一种需要编译后运行语言,编译后通过链接,整合其他依赖库,然后生成机器码(可执行文件),以后再运行,无需编译。在指定环境下编译运行,运行效率高
二、环境准备
1. 安装环境
C++ 是一种编译语言,需要安装编译环境。 此类环境有:
mingw
|cygwin
…官网: http://www.mingw.org
2. 安装开发工具
开发工具种类繁多,有
VS
|Dev-C++
|code lite
|clion
三、Hello World
1. 入门程序
#include<iostream>
int main (){
std::cout<<"hello world !"<<std::endl;
return 0 ;
}
2. main方法
- main函数是C++程序的入口函数,C++标准要求main()函数的返回值类型为int。 0 表示正常退出。
- 当main()函数的返回值为int,而函数内并没有出现return语句时,同样可以通过编译并正常运行。这是因为编译器在main()函数的末尾自动添加了return 0;的语句。所以,main()函数是C++程序经过特殊处理的函数。其他的返回值类型不是void的函数,如果没有使用return语句,编译器将报错。
- main函数也可以携带参数如:
int mian(){
return 0;
}
int main(int argc){
return 0 ;
}
3. 使用命令行编译C++代码
- Windows下编译
// 显示所有警告,使用c++14版本编译, 默认生成a.exe文件 ,linux 下生成的是a.o文件
g++ -Wall -std=c++14 main.cpp
g++ -Wall -std=c++14 main.cpp -o ab.exe
#include<iostream>
int main (){
std::cout<<"hello world !"<<std::endl;
reurn 0 ;
}
4. 在线编辑器
如果没有安装编译环境、也没有开发工具,也可以使用网页版的在线编辑器: http://cpp.sh
5. 注释
- 单行注释
python的单行注释 使用
#
, c++的单行注释使用//
- 多行注释
python的多行注释 使用
''' '''
, c++的多行注释 使用/* */
四、基本数据类型
数据类型 | 声明 |
---|---|
integer | int |
floating point | float |
double floating point | double |
character | char |
boolean | bool 0表示假, 非0表示真 |
valueless | void |
void
只会在一些场合下出现,我们几乎不会定义void类型的变量。一般会出现在方法的返回值,用于表示方法没有返回值。
1. 声明变量和初始化
整型和浮点数类型统称为算术类型.
变量类型 变量名字 = 变量值 ;
//先声明
int age ;
double price;
...
//后初始化
age = 10 ;
price = 20.3;
//声明及初始化
int age = 20 ;
在C++中,对于变量的初始化有好几种方式,如下:
int age ; //未初始化
int age = 21 ; // C 方式初始化
int age (21); //构造方法初始化
int age {21} ; //c++ 11标准开始的方式
2. 变量命名规则
- 可以包含字母 、 数字 和下划线
- 必须以字母或者下划线开始
注意:在C++中,大写字母和小写字母被认为是两个不同的字符。因此,sum和SUM是两个不同的变量名。一般地,变量名用小写字母表示,与人们日常习惯一致,以增加可读性。应注意变量名不能与C++的关键字、系统函数名和类名相同。
有效命名 | 无效命名 |
---|---|
Age | 2019_Age |
age | Age+1 |
_age | $age |
My_Age | My Age |
Int | return |
2.1变量的最大取值
#include <climits> //导入头文件
cout << "int类型最大的取值为:" <<INT_MAX<< endl; //2147483647
cout << "int类型最大的取值为:" <<INT_MIN<< endl; //-2147483648
2.2控制输出数值的进制(二进制,八进制,十进制)
int a = 100;
cout << "a=" << a << endl;//十进制输出
cout << hex;
cout << "a=" << a << endl; //十六制输出
cout << oct;
cout << "a=" << a << endl; //八进制输出
2.3输入输出的方式(cin和getline(),get())
//cin通过空格,制表符和换行来确定字符串(输入)的结束位置.
//测试输入:空格,换行,制表符
cout << "请输入你的名字" << endl;
string name;
cin >> name;
cout << "请输入你的年龄" << endl;
int age;
cin >> age;
cout << "我的名字是:" << name << " 我的年龄是:" << age << endl;
//控制台输出:
//请输入你的名字
//fuhong 24
//请输入你的年龄
//我的名字是:fuhong
//测试输入:cin.getline(),传入两个参数:1,保存的变量名称.2.输入的最大长度,3.停止符,到这个字符时停止读取
cout << "请输入你的名字" << endl;
string name;
cin.getline(name,20);
cout << "请输入你的年龄" << endl;
int age;
cout << "我的名字是:" << name << " 我的年龄是:" << age << endl;
return 0;
cin.getline()会丢弃换行符,cin.get()不会丢弃换行符.
//测试输入:cin.get(),传入两个参数:1,保存的变量名称.2.输入的最大长度,3.停止符,到这个字符时停止读取
cout << "请输入你的名字" << endl;
char name1[20];
cin.get(name1, 20).get(); //这里可以用.get()来处理换行符
cout << "请输入你的名字" << endl;
char name2[20];
cin.get(name2, 20);
cout << "我的名字是:" << name1 << " 他的年龄是:" << name2 << endl;
3. 常量
常量其实和变量没有多大区别, 有名字, 占据存储空间,可以是任何的基本类型,但只有一点不同,常量的值不允许变更。C++中的常量的声明需要使用
const
关键字,通常首首字母大写.而python中的常量默认约定是全大写表示。
const double Pi {3.1415926};
const int Months_in_year{12};
pi = 2.5 ; //将会出现编译错误
4. 变量的长度大小
我们可以使用sizeof 来获取某个变量的长度大小
例如:
double a = 20.0;
sizeof(a); //8
sizeof(int); //4
五、常见编码错误
- 忘记分号结尾
#include <iostream>
int main (){
int x
x = 5;
return 0;
}
- 声明和定义错误
忘记声明变量类型
#include <iostream>
int main (){
x = 5;
std::cout << x;
return 0;
}
- 命名空间错误
没有引入命名空间
#include <iostream>
int main (){
int x;
x = 5;
cout << x;
return 0;
}
- 库包含错误
一般出现这种情况都是因为上面忘记引入了库
int main (){
int x;
x = 5;
std::cout << x;
return 0;
}
六、逻辑控制
1. 条件控制
- python 和 c++ 对比 , 在C++ 中,数字也可以作为判断的条件,0 表示为假, 非0即为真
- c++ if语句语法
if (条件1) {
语句1;
语句2;
...
}else if (条件2) {
语句1;
语句2;
...
}else {
语句1;
语句2;
...
}
a. 关系运算符
python和c++完全一样
Operator | Python | C++ |
---|---|---|
equal | == | == |
not equal | != | != |
greater than | > | > |
less than | < | < |
greater than or equal | >= | >= |
less than or equal | <= | <= |
b. 逻辑运算符
Operator | Python | C++ |
---|---|---|
and | and | && |
or | or | || |
not | not | ! |
- python
height = 220
weight = 250
if height > 190 and weight > 200:
print('可以成为篮球运动员')
- c++
double height = 220
double weight = 250
if(height > 190 && weight > 200){
std::cout << "可以成为篮球运动员" << std::endl;
}
c. 三元运算符
一般来说,非真即假的情况下,如果表达式比较简单,通常会采用三元运算符来实现。在相比之下,c++的三元运算符比python的要简单些。
括号里面的表达式得出一个bool类型的值,若为true ,则执行expr1表达式 ; 如果是false,那么执行expr2表达式。
(cond_expr) ? expr1 : expr2 ;
int score = 100;
//python
result = 'A' if 100 > 90 else 'B'
print(result)
//c++
char result = score > 90 ? 'A' : 'B';
std::cout << result <<std::endl;
2. 循环控制
a. while 循环操作
除了大括号外,写法和操作几乎一样 , 休眠操作,使用Sleep()函数 , 不过要记得引入
<windows.h>
b. do-while循环
do-while循环和while循环都差不多,区别只是先执行后判断的顺序不同而已。
//一上来,先判断,再执行
while(cout < 15){
std::cout << "红灯还在亮着..." << std::endl;
//单位是毫秒
//_sleep(1000);
Sleep(1000);
cout++;
}
//先执行一次,然后在判断。
do{
std::cout << "红灯还在亮着..." << std::endl;
//cout++;
}while(cout > 5);
c. for 循环操作
3. switch 语句
许多语言中都包含switch,比如:java 、javascript 、php 等,而python是个例外,python没有switch。,实际上
switch
和if
语句块很相似, 它是完全的等价条件判断,但是一旦满足某个case
的条件,那么其他case
就不会再进行判断。
- 示例
int age = 11 ;
switch (age){
case 10:
cout <<"10岁" << endl;
break;
case 20:
cout <<"20岁" << endl;
break;
default:
cout <<"其他年龄" << endl;
break;
}
- 注意:
- swtich只能做等价条件判断,并且判断的只能是字符或者整型数字
- 一次只会执行一个case,如果没有匹配,会执行默认的default分支
- 如果某个case缺失break语句,那么会连带执行下一个case分支。
七、命名空间 namespace
假设这样一种情况,当一个班上有两个名叫 张三的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。
同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 a 的变量,在另一个可用的库中也存在一个相同的变量 a。这样,编译器就无法判断您所使用的是哪一个。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
1. 自定义命名空间
使用命名空间范围内的成员(变量、函数、类),需要使用 域操作符*(scope operator)
::
//深圳的张三
namespace shenzhen{
string name = "张三";
}
//武汉的张三
namespace wuhan{
string name = "张三";
}
int main() {
cout << shenzhen::name << endl;
return 0;
}
2. 使用using 指令
可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
#include <iostream>
using namespace std;
int main(){
count<<"hi c++" << endl;
return 0 ;
}
八、数组
1. python 和c++ 对比
c++
的数组实际上和python
的list
差不多,都是具有下标(索引) , 稍有不同的是,python
的list
不区分类型,而c++
的数组必须是同一类型。
2. 声明和初始化
数组是一系列相同类型的元素,放置在连续的内存位置,数组中的元素都可以通过索引来单独操作它们。查看某个变量存储地址可以使用 取地址符
&
a, 声明数组
仅仅声明,而没有初始化的数组,内部的元素无法得到保证,系统会随机进行赋值。
数组类型 数组名称 [元素个数];
int scores [5]; //表示5个元素的数组
b. 初始化
数组类型 数组名称 [元素个数]{初始化列表}
声明后再初始化
int scores [5];
scores[0] = 11;
scores[1] = 22;
scores[2] = 33;
scores[3] = 44;
scores[4] = 55;
声明并初始化
int scores [5]{100,89,95,70,80};
int socres [10]{88,75}; //剩下的都会以0占位,只初始化了前两位
int socres [10]{0};; // 表示10个长度的数组,每个元素都是0
//自动推算数组大小
int socres[]{22,33,44,55,66}; //数组长度没有指定,根据后面初始化长度来推断。
3. 访问数组
- 获取数组中的某个元素
可以使用索引来获取
array_name[element_index]
int scores [5]{100,89,95,70,80};
std::cout<<"数组的第一个元素是: "<< scores[0]<<std::endl;
std::cout<<"数组的第二个元素是: "<< scores[1]<<std::endl;
std::cout<<"数组的第三个元素是: "<< scores[2]<<std::endl;
std::cout<<"数组的第四个元素是: "<< scores[3]<<std::endl;
std::cout<<"数组的第五个元素是: "<< scores[4]<<std::endl;
//越界,不会报错,但是输出内容不是我们想看到的
std::cout<<"数组的第一个元素是: "<< scores[5]<<std::endl;
//修改指定位置的元素
scores[0] = 66;
- 遍历数组
c++的数组,并没有提供获取长度的方法,所以不能直接遍历。一种是直接指定遍历的长度,一种是通过代码计算出数组的长度 ,一种是使用c++11提供的基于范围的for循环
//定义数组
int scores[]{100,95,97,88,85,80,75};
//直接指定数组
for(int i = 0; i < 7; i++){
std::cout << scores[i] << std::endl;
}
//手动计算数组长度
int length = sizeof(scores) / sizeof(int);
for(int i = 0 ; i < length; i++){
std::cout << scores[i] << std::endl;
}
//c++11 提供的for循环 , 基于范围的for循环可以不再关心迭代器的概念,只需要关系容器中的元素类型即可,同时也不必显式的给出容器的开头和结尾。
for(int score : scores){
std::cout <<cores[i] << std::endl;
}
4. 多维数组
数组里面的元素存储的还是数组,即可称之为多维数组。二维数组是常见的多维数组,再多维度的数组阅读起来就稍微有点复杂了。
- 声明
数组类型 数组名称[x] [y]; //x可以认为是有多少行 , y可以认为是有多少列
- 初始化
九、字符串
字符串是最常用的一种数据类型了,在python中声明字符串和声明其他类型的数据一样,都非常的简单。但是在c++中,对于字符串的操作,相对来说要稍微复杂一些。
C++ 提供了以下两种类型的字符串表示形式:
- C 风格字符串
- C++ 引入的 string 类类型
1. 字符函数
用于操作字符的函数
2. C风格的字符串
C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
- 下面的声明和初始化创建了一个 “Hello” 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 “Hello” 的字符数多一个。
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
可以简写成:
char greeting[] = "Hello";
3. 字符串函数
C语言中提供了针对字符串操作的大量函数,不过在使用之前,需要先引入
#include <cstring>
函数 | 作用 |
---|---|
strcpy(s1, s2) | 复制字符串 s2 到字符串 s1 |
strncpy( s1, s2 , n ) | 赋值字符串s2 的 n个长度字符到s1 ( 默认从0位置开始 ) |
strcat(s1, s2) | 拼接 s2 到 s1 的末尾 |
strlen(s1) | 返回字符串s1的长度 |
strcmp(s1, s2) | 比较字符串是否一致 |
strchr(s1, ch) | 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
strstr(s1, s2) | 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
strrev(s1) | 反转字符串 , 针对char数组 |
reverse(s1) | 反转字符串 ,针对string字符串 ,需导入 #include<algorithm> |
4. C++的字符串
C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。需要引入
#include <string>
- 声明和初始化
//引入string库
#include <string>
using namespace std;
int mian(){
string s1;
string s2 {"北京"};
string s3{s2};
s1 = s3;
return 0 ;
}
5. 字符串操作
- 拼接字符串
c++的字符串拼接,使用起来比较方便,直接使用
+
操作即可。
string part1 {"c++"};
string part2 {" is a powerful"};
string sentence ;
sentence = part1 + part2 ;
- 获取指定位置的字符
可以使用[]和 at()操作字符串
string s1 {"i love c++"};
cout << s1[3]<<endl;
cout << s1.at(0) << endl;
- 遍历字符串
string s1 {"abcdef"};
for(char s : s1){
cout << s << endl;
}
for(int s : s1){
cout << s <<endl;
}
- 字符串比较
字符串也是可以比较大小的。
string s1{"Apple"};
string s2{"Banana"};
string s3 {"kiwi"};
string s3 {"apple"};
string s3 {s1};
s1 == s5 // true
s1 == s2 // false
s1 != s2 // true
s1 < s2 // True
s1 > s2 // false
s1 == "Apple" // false
- 截取字符串
substr(开始索引, 截取长度);
string s1 {"This is a test"};
cout << s1.substr(0 , 4) ; // This
- 获取字符(字符串)在字符串中的索引
find(搜索的字符)
string s1 {"This is a test"};
cout << s1.find("This") ; // 0
cout << s1.find("is") ; // 2
cout << s1.find("test") ; // 10
- 获取字符串长度
length() : 返回字符串长度
一、Vector
Vector其实很大程度上和数组一样,只是,数组是固定长度,而vector是不定长度(动态增长)。 假设我们需要记录明年的测试成绩,但是我们并不知道明年会有多少个学生。那么可以有两种选择,定义一个固定长度的数组,这个长度超过假设的长度, 另一种办法就是使用动态数组,比如是: vector
1. 什么是Vector
在C++STL(标准模板库)中的一个容器,可以看成是对容器的一种扩展。
在运行时可以改变长度
与数组具有相似的语法
相比数组更高效 , 提供越界检查
2. 声明和初始化
- 声明
#include <vecotr>
vector <char> vowels;
vector <int> test_score;
// =========================
vector <char> vowels(5); //声明一个初始大小为5的char类型vector
vector <int> test_score(10);
- 初始化
//数组定义
int test_score []{100,99,18,81}
vector <char> vowels {'a' , 'e' , 'i' , 'o' ,'u'};
vector <int> test_score{ 100 ,98,95,90,80};
vector <double> temperatures{26,20.7};
3. 访问vector
- 数组的语法
#include <vector>
vector<int> test_score {100,90,85};
cout << "第一个成绩是: " <<test_score[0] << endl;
cout << "第二个成绩是: " <<test_score[1] << endl;
cout << "第三个成绩是: " <<test_score[2] << endl;
cout << "第三个成绩是: " <<test_score[3] << endl; //不会检查越界
- vector 的语法
#include <vector>
vector<int> test_score {100,90,85};
cout << "第一个成绩是: " <<test_score.at(0) << endl;
cout << "第二个成绩是: " <<test_score.at(1) << endl;
cout << "第三个成绩是: " <<test_score.at(2) << endl;
cout << "第三个成绩是: " <<test_score.at(3) << endl; //抛出越界异常
- 修改vector中的元素
vector<int> test_score {100,90,85};
test_score.at(0) = 73;
- 往vector中追加元素
vector<int> test_score {100,90,85};
test_score.push_back(80); // 100 , 90 , 85 , 80
test_score.push_back(95); // 100 , 90 , 85 , 80 , 95
//vector会自动分配它要求的大小
-
删除vector中的元素
vector<int > scores{60,65,50,70,80}; auto i = scores.begin(); while(i != scores.end()){ if(*i.base() == 50){ scores.erase(i); } i++; }
-
越界检查
只要当我们使用了vector的语法去获取超出索引的元素时,就会抛出异常。而使用数组的语法去获取元素,则不会进行越界检查
- 遍历vector
//使用下标遍历
vector<int> scores{ 100 ,95 ,88 ,80 ,75};
for (int i = 0; i < scores.size(); ++i) {
cout << scores[i] << endl;
}
//基于范围for遍历
vector<int> scores{ 100 ,95 ,88 ,80 ,75};
for(int score : scores){
cout << score << endl;
}
4. 二维Vector
二维vector和二维数组实际上差不太多,二维数组是数组里面装的是数组,二维vector指的是vector里面装的还是vector,在未来碰到矩阵相关的存储操作,多半使用vector来作为媒介。 比如下面的例子,演示了使用vector来存储3个班的考试成绩。每个班的成绩单独使用一个vector来存储。
//声明并初始化vector
vector<vector<int>> scores {
{95,77,80,85},
{58,89,93,100},
{69,73,81,97}
};
for (int i = 0; i < scores.size(); ++i) {
for (int j = 0; j < scores[i].size(); ++j) {
cout << scores[i][j] <<"\t" ;
}
cout << endl;
}
二、 函数
在大多数地方,c++ 和 python的函数是一样的,都是用来包裹定义好的语句,避免重复拷贝粘贴。不过还是有些许不一样的地方。
- python的函数是以回车换行结尾,c++的函数是以 大括号结尾
- python的函数通常使用缩进方式来表示函数体, ,c++使用大括号区域来表示
- python是动态类型语言,而c++是静态类型语言,所以有时候需要像声明变量一样,声明函数。
1. 定义函数
函数的定义一般可以包含以下几个部分:
方法名称
、方法参数
、返回值
、方法体
如:
返回值 方法名称(参数){
方法体
返回值
}
int function_name(){
statements(s);
return 0 ;
}
- 调用函数
void say_hello(){
count << "hello" << endl;
}
int main(){
say_hello();
return 0 ;
}
2. 函数原型
一般来说,c++的函数一般包含声明和定义两个部分。因为c++是静态类型语言,程序属于自上而下编译,所以在使用函数前,必须先表示函数的存在,告诉编译器函数所需要的参数以及函数的返回值是什么。
//函数声明 ,也叫函数原型 并不知道这个函数具体是如何实现的。只是有一些基本架子而已。
int add (int a , int b);
int main(){
add(1 ,2);
return 0 ;
}
//函数定义 ,函数的真正实现。
int add(int a , int b){
return a + b ;
}
1. 关于函数声明
也并不一定要在main函数前面先声明函数,然后main的后面定义函数 。我们可以把函数直接定义到main函数的前面去,但是还是建议在对函数操作时,声明和定义都带上。
//函数定义 ,函数的真正实现。
int add(int a , int b){
return a + b ;
}
int main(){
add(1 ,2);
}
2. 分离式编译
一般说来,函数的声明 ( 函数原型 )通常都会放到头文件中,之所以称之为头文件是因为它总是在main函数的前面就引入进来。头文件一般以 .h 或者 .hpp 结尾,通常用于 写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现
- math.h
为了能够让声明和定义能够快速的被关联上,通常它们的名称会被定义成一样的,这已经成为了一种默认的规定
//函数声明
int add (int a , int b);
- math.cpp
#include "math.h"
//函数定义 ,函数的真正实现。
int add(int a , int b){
return a + b ;
}
- main.cpp
#include <iostream>
#include "math.h" //这里使用"" 表示从当前目录查找
int main(){
add(1 ,2);
return 0 ;
}
3. 多返回值
与python不同的是,但是在c++ 函数只能有一个返回值,并且在函数声明时必须表明返回值的类型 ,如果函数没有返回值,那么一般使用 void 来表示返回值类型 。 在python中,函数是可以具有多个返回值的,返回值类型无需表示
- python
def add(a, b):
return a + b, a + 10
- c++
int add(int a , int b){
return a + b;
}
4. 函数重载
在许多语言中,经常会见到两个或者两个以上的函数名称是一样的,当然他们的 参数个数 或者 参数类型 或者是 参数的顺序 是不一样的。这种现象有一个学名叫做 重载 overload, 由于python属于动态类型语言,不区分数据类型,参数可以是任意类型,所以它没有重载。
- c++
int add(int a , int b){
return a + b ;
}
int add(int a , int b , int c){
return a + b + c;
}
int add(double a , double b){
return a + b ;
}
int main(){
add(3, 3);
add(3, 3, 3);
add(2.5 , 2.5);
return 0 ;
}
5. 函数参数
python的函数,在传递参数的时候,有可变对象和不可变对象的现象,那么在C++里面也有类似的说法。只不过是另一种说辞罢了。
python中传递不可变对象,在C++中,对应的是值的拷贝,也就是传递的只是数据的一份拷贝而已。在函数内部修改数据,并不会改变外部数据
python中传递可变对象,在c++中,对应的是引用传递,也就是传递的是对象的引用,而不是拷贝。在函数内部修改数据,会导致外部数据也发生改变。
1. 值拷贝传递
C++默认情况下,处理函数参数传递时,多数使用的是值的拷贝,少数部分除外。
void scale_number(int num);
int main(){
int number{1000};
scale_number(number);
//打印number 1000
cout << number << endl;
return 0 ;
}
void scale_number(int num){
if(num > 100)
num = 100;
}
2. 传递数组
函数的参数除了能传递普通简单的数据之外,数组也是可以传递的。但是数组稍微有点特殊,这里多做讲解。
- 前面提过,形参实际上就是实参的一份拷贝,就是一个局部变量。
- 数组的数据太大,如果都进行拷贝,那么比较麻烦,也造成了浪费
- 所以实际上传递数组的时候,并不会进行整个数组的拷贝,而只是传递数组的第一个元素内存地址 (指针 ) 进来。
- 数组的数据还是在内存中,只是把第一个元素(也就是数组的起始)内存地址传进来而已。
- 这就造成了函数的内部根本无法知道这个数组的元素有多少个。
using namespace std;
//传递数组长度
void print_array(int number[] , 5 );
int main(){
//声明数组
int array []{1,2,3,4,5};
//打印数组
print_array(array , 5);
return 0 ;
}
//传递数组,打印数组
void print_array(int array[] , int size){
for (int i {0} ; i < size ; i++){
count << array[i] << endl;
}
}
3. 传递引用
目前为止,我们所有函数的参数传递,都是对数据进行了一份拷贝(数组除外)。那么在函数的内部是不能修改值的,因为这仅仅是一份值得拷贝而已(函数外部的值并不会受到影响)。如果真的想在函数内部修改值,那么除了数组之外,还有一种方式就是传递
引用
。
void scale_number(int &num);
int main(){
int number{1000};
scale_number(number);
//打印number100
count << number <endl;
return 0 ;
}
void scale_number(int &num){
if(num > 100)
num = 100;
}
4. 练习: 传递vector
既然掌握了使用传递引用,以便修改外部数据,那么来修改下传递vector吧。vector和普通的数据一样,传递的时候,做的是值得拷贝。数据有点浪费,只想传递引用,怎么办呢?
void chage_data(vector<int> &v);
int main(){
vector<int> scores {95,79,53,85,95,90};
chage_data(scores);
for(int s : scores){
cout << s << endl;
}
return 0 ;
}
void chage_data(vector<int> &v){
for(int i = 0 ; i<v.size() ; i++){
if(v[i] < 60 ){
v[i] = 60;
}
}
}
6. 函数是如何被调用工作的
- 函数是使用函数调用栈来管理函数调用工作的。
- 类似盒子的栈
- 遵循后进先出
- 可以往里面执行压栈和出栈动作(push 和 pop)
- 栈的结构和激活记录
- 函数必须把它的返回值返回给调用它的函数(A —> B)
- 每次函数的调用都需要创建一次激活记录,然后把它压入栈中(push)
- 当一个函数被调用完毕的时候,就需要从栈中弹出(pop)
- 函数的参数以及内部的局部变量都是存储在栈中。
- 函数栈有可能抛出栈溢出异常(Stack Overflow)
- 一旦函数调用栈中被push进来的函数记录过多,就有可能出现。(例如:无限循环调用)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eBRGeX51-1581313773494)(img/18.png)]
void func2(int &x , int y , int z){
x +=y+z;
}
int func1(int a , int b){
int result{};
result += a + b;
func2(result , a , b );
return result ;
}
int main (){
int x{10};
int y{20};
int z{};
z = func1(x , y );
//打印值为60;
cout << z << endl;
return 0 ;
}
7 . 内联函数
函数可以使我们复用代码,但是一个函数的执行,需要开辟空间、形参和实参进行值得拷贝,还要指明函数返回、以及最后回收释放资源的动作,这个过程是要消耗时间的。
- 作为特别注重程序执行效率,适合编写底层系统软件的高级程序设计语言,如果函数中只有简单的几行代码,那么可以使用
inline
关键字来解决了函数调用开销的问题
inline int calc_Max (int a, int b)
{
if(a >b)
return a;
return b;
}
增加了 inline 关键字的函数称为“内联函数”。内联函数和普通函数的区别在于:当编译器处理调用内联函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插人调用语句处,就像整个函数体在调用处被重写了一遍一样。
有了内联函数,就能像调用一个函数那样方便地重复使用一段代码,而不需要付出执行函数调用的额外开销。很显然,使用内联函数会使最终可执行程序的体积增加。以时间换取空间,或增加空间消耗来节省时间,这是计算机学科中常用的方法。
8. 范围规则
在学习过程,我们会定义很多变量或者引用、这些变量由于定义的位置不同,所以它们的作用域范围也不同。
- c++的的变量作用规则是:从它开始定义的地方起作用。
- 使用static关键字会让它变得不一样
- 本地或者块范围
- 全局范围
- 本地或者代码块的作用域
- 通常定义在 {} 中
- 函数参数具有本地变量作用域
- 在{}中声明的,只能在{}中有效
- 函数的局部变量只有在函数执行的时候才会有效
- 单独代码块
int main(){
int num{10};
int num1{20};
cout << num << num1 << endl;
{
int num{100};
count << "num = "<< num << endl;
count << "num1 = "<< num1 << endl;
}
}
- 函数中局部变量
int num{300};
void local_example(int x){
int num{1000};
count << "num =" << num << endl;
num = x ;
count << "num =" << num << endl;
}
- 静态本地变量
static int value{10};
- 只会初始化一次
- 重复调用函数也只会初始化一次。
void static_local_example(){
static int num{100};
cout << "num ="<< num << endl;
num+=100;
cout << "num ="<< num << endl;
}
int main(){
static_local_example();
static_local_example();
return 0 ;
}
- 全局变量
通常声明在所有函数和类的外部 ,若存在局部变量和全局变量同名情况下,可以使用 域操作符
::
来访问全局变量
using namespace std;
int age = 99;
int main(){
int age =18 ;
cout << ::age << endl;
return 0 ;
}
三、 结构体
结构体是一个由程序员定义的数据类型,可以容纳许多不同的数据值。在过去,面向对象编程的应用尚未普及之前,程序员通常使用这些从逻辑上连接在一起的数据组合到一个单元中。一旦结构体类型被声明并且其数据成员被标识,即可创建该类型的多个变量,就像可以为同一个类创建多个对象一样。 结构体是由C语言发明出来的,它和类其实没有差多少,只是它的所有成员默认都是public公开。
1. 定义结构体
声明结构体的方式和声明类的方式大致相同,其区别如下:
- 使用关键字 struct 而不是关键字 class。
- 尽管结构体可以包含成员函数,但它们很少这样做。所以,通常情况下结构体声明只会声明成员变量。
- 结构体声明通常不包括 public 或 private 的访问修饰符。
- 类成员默认情况是私有的,而结构体的成员则默认为 public。程序员通常希望它们保持公开,只需使用默认值即可。
struct Student{
string name;
int age;
}
Student s1 ;
s1.name = "zhangsan";
s1.age = 18 ;