嵌入式学习-C嘎嘎-Day01
嵌入式学习-C嘎嘎-Day01
1.C++简介
2. C++特点
3. 面向对象
4. 环境搭建
5. 创建并运行项目
6.引用 reference
6.1 概念
6.2 性质
6.3 引用参数
7. 赋值
8. 输入
9. 字符串类型 string
10. 函数
10.1 内联函数 inline
10.2 函数重载 overload
10.3 函数参数的默认值(缺省值)
1.C++简介
1983年,贝尔实验室(Bell Labs)的Bjarne Stroustrup发明了C++。 C++在C语言的基础上进行了扩充和完善,是一种面向对象程序设计(OOP)语言。
Stroustrup说:“这个名字象征着源自于C语言变化的自然演进”。还处于发展完善阶段时被称为“new C”,之后被称为“C with Class”。C++被视为C语言的上层结构,1983年Rick Mascitti建议使用C++这个名字,就是源于C语言中的“++”操作符(变量自增)。而且在共同的命名约定中,使用“+”以表示增强的程序。
常用于系统开发,引擎开发、嵌入式开发等应用领域, 至今仍然是最受广大程序员喜爱的编程语言之一。
2. C++特点
- 在支持C语言的基础上,全面支持面向对象编程。
- 编程领域广泛,功能强大(最强大的编程语言之一)。
- 标准保持更新,目前常用的基础标准为ISO C++98、ISO C++11
- 为数不多的支持底层操作的面向对象语言
- 在面向对象编程语言中执行效率极高
3. 面向对象
面向对象的重点术语:
- 类
- 对象
- 面向对象的三大特性:封装 → 继承 → 多态
【思考】如果要把大象装进冰箱,应该怎么做?
面向过程的编程思维:
1. (我)打开冰箱门
2. (我)把大象装进去
3. (我)关上冰箱门
面向过程的语言,关注的重点是“算法”,可以认为“算法”是一系列有序的步骤,只要按照这个步骤做事,就能得到预期的结果,因此面向过程语言编写的程序,通常是一系列有序的语句,这种思维方式更接近于计算机执行命令的本质。
面向过程的语言具有高执行效率,低开发效率的特点。
面向对象的编程思维:
1. 把大象和冰箱拟人化
2. 安排任务给大象和冰箱
3. 大象和冰箱自动完成装配
面向对象的语言,关注的重点是“对象”之间的关系,对象的本质是一系列由于某种联系聚焦在一起的数据,这种思考方式更接近人类社会运行的本质,因为对过程的操作变成了对对象的管理。
面向对象的语言具有高开发效率,低执行效率的特点。
4. 环境搭建
C++支持多种IDE(集成开发环境),本次授课使用的是Qt Creator:
使用的原因是Qt开发还可以使用这套环境。
但是纯C++开发也有一些其他的选择,个人可以根据自己的情况灵活选用:
- CLion
- VS Code
Qt Creator的安装和配置过程如下:
1. 直接双击安装包开始安装,安装过程中一直下一步,只需要注意不要使用任何包含中文的路径!!!
2. 安装后启动,重新配置编码。
3. 配置完成后重新启动Qt Creator,就可以创建C++项目了。改编码的目的是让C++支持中文显示,需要注意的是,C和C++本身不支持中文处理!!
常用快捷键:
- Ctrl + R 运行
- 先Ctrl + A,再Ctrl + I 代码对齐
- Alt + 0 显示/隐藏边栏
- Alt + ← 光标移动到上次编辑的位置
- Alt + → 光标移动到下次编辑的位置
5. 创建并运行项目
1. 在Windows中搜索Qt Creator并启动。
2. 点击
3. 在弹出的窗口中,按照下图所示进行操作。
4. 在弹出的窗口中配置项目参数。
5. 直接使用默认的编译器。
6. 在项目管理界面,直接点击完成。
7. 项目创建完成后,自带三个文件。
- .pro 项目配置文件
配置项目参数,例如C++11的完整支持,需要在此文件中添加下面的配置项:
QMAKE_CXXFLAGS += -std=c++11
- .pri 无视
- main.cpp C++的主代码文件
C++的源文件格式为.cpp,后期学习过程中主要在此文件中编程。
// 引入C++标准输入输出头文件
// 不带.h且使用<>的头文件为C++内置
#include <iostream>// 使用标准名字空间(后续讲,别删)
using namespace std;/**
* @brief main 主函数,程序的入口
* @return 返回值
*/
int main()
{
// 连续输出一个字符串和换行符(endl(line))
cout << "Hello World!" << endl;
// 测试中文输出
cout << "你好,世界!" << endl;
return 0;
}
8. 编程完成后,点击左下角的,可以构建并运行项目。
9. 正常情况下,可以支持中文输出:
6.引用 reference
6.1 概念
引用是变量的“别名”,操作引用与操作原变量完全相同。
#include <iostream>using namespace std;int main()
{int a = 1;int& b = a; // b是a的引用(别名)
b++; // 相当于a++
cout << a << endl; // 2
cout << b << endl; // 2
cout << &a << " " << &b << endl; // 0x61fe88 0x61fe88return 0;
}
6.2 性质
所有的性质本质上都是引用的概念导致的。
- 引用一旦建立,就不能再次成为其他变量的引用。
#include <iostream>using namespace std;int main()
{int a = 1;int& b = a; // b是a的引用(别名)int c = 2;
b = c; // 把c的值复制给b(a)
cout << a << " " << b << " " << c << endl; // 2 2 2
cout << &a << " " << &b << " " << &c << endl; // 0x61fe78 0x61fe78 0x61fe74
c++;
a--;
cout << a << " " << b << " " << c << endl; // 1 1 3return 0;
}
- 声明引用时,必须初始化。
- 声明引用时,初始化的值不能为NULL
- 声明引用时,如果初始值为数值,此时引用必须使用const修饰,表示引用的值不能变(常量引用,常引用)。
#include <iostream>using namespace std;int main()
{
// int& b = 123; 错误const int& b = 123;
// b++; 错误
cout << b << endl;return 0;
}
- 可以对引用建立指针,此时指针指向的还是原变量。
#include <iostream>using namespace std;int main()
{
int a = 1;
int &b = a;
int* c = &b; // c → b(a)
cout << *c << endl; // 1
a++;
cout << *c << endl; // 2 return 0;
}
- 可以对指针建立引用
#include <iostream>using namespace std;int main()
{
int a = 1;
int* b = &a;
int*& c = b; // c是b的引用
a++;
cout << a << " " << &a << endl; // 2 0x61fe88
cout << *c << " " << c << endl; // 2 0x61fe88 return 0;
}
- 可以用const修饰引用,表示常引用,此时可以修改原变量的值,间接修改常引用的值。
#include <iostream>using namespace std;int main()
{
int a = 1;
const int& b = a; // b是a的常引用
// b++; 错误
a++;
cout << b << endl; // 2 return 0;
}
引用与指针的区别?
答案不唯一,但是你要能说上几条,随着学习加深自己总结,例如:
1. 引用必须初始化,指针不必.
2. 引用初始化后不能被改变,指针可以改变所指的变量.
3. 不存在指向空值的引用,但是存在指向空值的指针.
6.3 引用参数
函数的参数输入两个int值,函数功能为交换这两个int值。
#include <iostream>using namespace std;/**
* @brief swap1 错误
* @param a
* @param b
*/
void swap1(int a,int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}/**
* @brief swap2 指针,正确但繁琐
* @param a
* @param b
*/
void swap2(int* a,int* b)
{
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}/**
* @brief swap3 引用,正确且简单
* @param a
* @param b
*/
void swap3(int& a,int& b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}int main()
{
int a = 1;
int b = 2;
swap1(a,b);
cout << a << " " << b << endl; // 1 2
swap2(&a,&b);
cout << a << " " << b << endl; // 2 1
swap3(a,b);
cout << a << " " << b << endl; // 1 2 return 0;
}
引用作为参数可以使传递的效率提高,因为不产生副本,但是也可能会影响本身不该修改的外部参数。
引用参数在应该能被添加为const的情况下,尽量添加const修饰,以达到引用参数的安全性。
#include <iostream>using namespace std;/**
* @brief show 展示变量
* @param a
*/
void show(const int& a)
{
cout << a << endl;
}int main()
{
int a = 1;
show(a);
cout << a << endl; return 0;
}
7. 赋值
C++增加新的赋值语法:
#include <iostream>using namespace std;int main()
{
// C++的赋值写法
int a(1); // 等效 int a = 1;
int b(a); // 等效 int b = a;
cout << a << " " << b << endl; return 0;
}
C++11在上面的写法基础上,增加了数据窄化警告功能。
8. 输入
C++使用cin进行键盘输入,与cout相同,也支持支持连续输入。连续输入时,可以通过回车或空格间隔多个输入内容。
#include <iostream>using namespace std;int main()
{
string a; // string是C++的字符串类型
int b;
char c; cout << "请依次输入一个字符串、整数和字符:" << endl;
// 通过cin连续输入数据给abc
cin >> a >> b >> c;
cout << "您输入的数据为:" << endl;
cout << a << endl;
cout << b << endl;
cout << c << endl; return 0;
}
如果字符串需要包含空格,则可以通过getline函数直接获取一行的输入内容。
#include <iostream>using namespace std;int main()
{
string a; cout << "输入一行字符串,可以包含空格:" << endl;
// 获取整行cin的输入,到a中
getline(cin,a);
cout << "您输入的内容是:" << endl;
cout << a << endl;
return 0;
}
9. 字符串类型 string
string不是内置的基本数据类型,而是C++标准库std中的一个字符串类,因此在使用时需要手动引入引入头文件#include <string>,而不是<string.h>
string在绝大多数情况下可以替代char*,不用担心内存是够足够、字符串长度等问题,内部集成了大量的处理函数,可以完成绝大多数情况下字符串的处理。
可以通过at函数和[]分别配合下标取出单字符,但是更推荐at函数,因为at函数更安全,at会针对范围越界强行终止程序运行:
#include <iostream>using namespace std;int main()
{
string a;
a = "fsd";
cout << a << endl;
a = "fgkjdgfjkdg";
cout << a << endl;// 获取字符串长度
cout << a.size() << endl;
cout << a.length() << endl;// 取出单字符
cout << a[1] << endl; // g
cout << a.at(1) << endl; // g// for循环遍历for(int i = 0;i<a.size();i++)
cout << a.at(i) << " ";
cout << endl;// C++11 新增for-each遍历for(char i:a)
cout << i << " ";
cout << endl;// 迭代器(后续讲解)// cout << a[10086] << endl; // 结果不定
// cout << a.at(10086) << endl; // 终止 cout << "主函数结束" << endl;return 0;
}
可以借助sstream类型完成字符串和数字在C++中的转换:
#include <iostream>
#include <sstream> // 字符串流using namespace std;int main()
{
string s = "123";
int a;
// int a = (int)s; 错误 // string → int
istringstream iss(s);
iss >> a;
cout << a << endl; // 123 // int → string
stringstream ss;
ss << ++a;
s = ss.str();
cout << s << endl; // "124" return 0;
}
10. 函数
10.1 内联函数 inline
C++中内联函数的主要目的是取代C中宏定义的函数,内联函数需要使用关键字inline放在函数定义(不是声明)的前面。
内联函数在编译是,直接展开函数体到主函数中,因此可以提升程序的执行效率,因为它消除了函数调用的开销。
建议将代码长度较短(5行以内)且频繁使用的函数设置为内联函数,同时不能包含复杂的控制语句(循环或判断),inline关键字只是一个建议性的效果,编译器是否展开函数体取决于自身判断。
后续学习的成员函数默认使用inline修饰。
#include <iostream>using namespace std;inline void test1()
{
cout << "一个普通的函数" << endl;
}// 先声明
void test2();// 再定义
inline void test2()
{
cout << "其实也是一个普通函数" << endl;
}int main()
{
test1();
test2(); return 0;
}
10.2 函数重载 overload
C++允许同一个函数名编写多个函数,这种用法就是函数重载。
编译器区分不同的函数是通过函数签名决定的,函数签名包括:
- 函数名称
- 参数类型
- 参数个数
三者均相同时,编译器会认为这是同一个函数,与返回值类型无关。
不同的函数具有相同名称,但参数不同(类型或个数),这些函数会构成重载的关系。
#include <iostream>using namespace std;void print(string s)
{
cout << "A:" << s << endl;
}void print()
{
cout << "B" << endl;
}void print(int a)
{
cout << "C:" << a << endl;
}//int print(int a) 二义性错误
//{
// cout << "D:" << a << endl;
// return a;
//}int main()
{
print(); // B
print(1); // C
print("nihao"); // A return 0;
}
除了上面的普通函数可以重载外,后续学习的成员函数和构造函数也能重载,但是析构函数不能重载。
10.3 函数参数的默认值(缺省值)
C++中允许给函数的参数设定默认值,调用函数时,如果传入参数,传入的参数会覆盖默认值;如果不传入参数,则使用默认值作为参数的数值。
当函数具有多个参数时(1个以上),默认值的设定需要遵循“向右原则(向后原则)”——当第n个参数设定默认值后,n+1一直到最后一个参数都需要设定默认值。
#include <iostream>using namespace std;void func1(int a = 0)
{
cout << "func1:" << a << endl;
}//void func2(int a,int b=1,int c) 错误
//{
// cout << a << b << c << endl;
//}void func3(int a,int b=1,int c=2)
{
cout << a << b << c << endl;
}int main()
{
func1(); // func1:0
func1(2); // func1:2
// func3(); 错误
func3(6); // 612
func3(6,6); // 662
func3(8,8,8); // 888 return 0;
}
需要注意的是,如果函数的声明与定义分离,建议默认值只在声明或定义处设定,不要同时设定。
#include <iostream>using namespace std;void test1(int a = 1);void test1(int a)
{
cout << a << endl;
}void test2(int a);void test2(int a = 2)
{
cout << a << endl;
}//void test3(int a = 3);//void test3(int a = 4)
//{
// cout << a << endl;
//}int main()
{test1();test2();
// test3(); 错误return 0;
}
尽量不要同时使用函数重载和参数默认值,因为很容易出错。
#include <iostream>using namespace std;void method()
{
cout << "A" << endl;
}void method(int a = 0)
{
cout << "B" << endl;
}int main()
{method(3);
// method(); 错误:二义性return 0;
}