1.C++与C语言的关系
C++保留了C语言的大部分语法,并增加了类、对象、集成、多态、函数重载等能力。已经学过C,因此本课程不重新讲变量、循环、数组和指针,而是重点学习C++对程序结构的组织方式。
2.C++特点
- 在支持C语言的基础上,全面支持面向对象编程。
- 编程领域广泛,功能强大(最难的编程语言之一)。
- C++语言的标准一直保持着更新,本次课程主要以ISO C++98标准为主。
- 为数不多的支持底层操作的面向对象编程语言。
- 在面向对象的语言中执行效率极高。
3.面向对象编程的重要知识点
- 类
- 对象
- 封装
- 继承
- 多态
封装、继承、多态也被称为面向对象编程的三大特性。
4.面向过程与面向对象的区别(重点)
【例子】:把大象装进冰箱需要几步?
- 打开冰箱
- 把大象放进去
- 关闭冰箱
程序会编写成一组函数,数据在函数之间传递。
面向对象中关注"对象"。
- 大象具有移动等行为
- 冰箱具有打开、关闭、保存物品的功能。
- 程序通过对象之间的调度完成任务。
面向过程
主要关注"怎么做"
把程序拆成一步一步的流程、函数,以步骤为核心,数据和函数分离。
代表语言C语言
面向对象
关注"谁来做"
把程序拆解为对象(实体),将数据(属性)+操作数据的方法封在一起。
代表语言:Java、C++、python
面向过程:按照流程解决问题
面向对象:把事物拆分类别,依靠类别的实体协作解决问题。
5.环境搭建
单纯论C++的开发环境,没有严格的要求,为了学习的方便,直接使用后面课程的开发环境。即QtCreator。
安装完成后为了使其支持中文输出,更改下面的编码:


设置完成后,重新启动Qt Creator,就可以新建一个C++的项目了。
1.点击

2.在弹出的窗口中,按照下图所示进行操作

3.在弹出的窗口中,输入项目名称,在设定项目路径,最后点击下一步。需要注意的是不得出现任何中文的字符和特殊字符。

4.在弹出的窗口中,直接点击下一步即可。

5.在项目管理界面,直接点击完成

6.可以看到新项目中包含了两个文件

.pro文件为项目配置文件,通常无需手动编辑
.cpp文件为C++的源代码文件,用于编写C++代码。
cpp
// 包含头文件,标准输入输出流
// 在C++源码中,头文件不需要包含.h
#include <iostream>
// 使用标准名字空间,不要删
using namespace std;
// 主函数,程序的入口
int main()
{
// 连续输出一个字符串和换行符
cout << "Hello World!" << endl;
// 返回一个0
return 0;
}
```
点击左下角的小三角(快捷键:ctrl+R),即可运行项目

补充几个快捷键的使用技巧:
1、ctrl+c 复制
2、ctrl+v 粘贴
3、ctrl+R 编译+运行
4、Alt+0 显式和隐藏边栏
5、ctrl+A 全选 ,再点击ctrl+i 对齐。 本页全部代码排版
6、ctrl+F搜索+替换
7、ctrl+z 回滚
8、ctrl+s 保存
6.键盘输入
可以使用cin把用户再命令行中输入的内容赋值到变量中。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
int a;
// C++的字符串类型
string str;
cout << "请输入一个数字和字符串" << endl;
cin >> a >> str; // 接收键盘的输入,一个整数和一个字符串,可以连续操作
cout << a << " " << str << endl;
return 0;
}
```
如果cin输入的字符串需要包含空格,则可以使用下面的方式:
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
int a;
// C++的字符串类型
string str;
cout << "请输入一个数字和字符串" << endl;
cin >> a >> str; // 接收键盘的输入,一个整数和一个字符串,可以连续操作
cout << a << " " << str << endl;
return 0;
}
```
7.引用(掌握)
7.1概念
指针相当于对于某一目标变量起一个别名。
操作引用与操作变量完全相同。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
// 表示b是a的引用
int &b = a;
cout << a << " " << &a << endl; // 1 0x61fe88
cout << b << " " << &b << endl; // 1 0x61fe88
return 0;
}
```
7.2引用的特点
可以改变引用的值,但是不能再次成为其他变量的引用。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
// 表示b是a的引用
int &b = a;
cout << a << " " << &a << endl; // 1 0x61fe88
cout << b << " " << &b << endl; // 1 0x61fe88
return 0;
}
```
声明引用时,必须要做初始化。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
// 表示b是a的引用
int &b = a;
cout << a << " " << &a << endl; // 1 0x61fe88
cout << b << " " << &b << endl; // 1 0x61fe88
return 0;
}
```
声明引用的时候,不能直接初始化为NULL,如果想要给NULL取别名,需要使用const关键字修饰。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
// int &b = NULL; // 错误不能直接给NULL取别名
const int &b = NULL; // 如果要给NULL取别名,需要const
cout << a << " " << &a << endl; // 3 0x61fe88
cout << b << " " << &b << endl; // 3 0x61fe88
return 0;
}
```
声明引用的时候,初始化的值可以是纯数值,但是需要使用const进行修饰,表示改引用为常量引用,这样引用的值不可变。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
const int &b = 12;
// b++; // 错误常量引用的数值无法被改变
cout << b << " " << &b << endl;
return 0;
}
```
7.3 引用作为函数参数
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
const int &b = 12;
// b++; // 错误常量引用的数值无法被改变
cout << b << " " << &b << endl;
return 0;
}
```
7.4 const引用参数
如果函数只想读取参数,不修改参数,可以使用const引用。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
const int &b = 12;
// b++; // 错误常量引用的数值无法被改变
cout << b << " " << &b << endl;
return 0;
}
```
8.string字符串类(掌握)
string不是C++的基本数据类型,它是一个C++标准库中的字符串类,使用时需要引入头文件#include<string>不包含.h。
string在绝大多数的情况下可以代替C语言的字符数组,不必担心内存是否足够和字符串处理函数,可以完成各种情况下的字符串处理功能。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
const int &b = 12;
// b++; // 错误常量引用的数值无法被改变
cout << b << " " << &b << endl;
return 0;
}
```
两种方式都可以,但是在C++中更推荐使用at函数,原因是at函数更安全。但是\[\]的方式效率更高。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
string str = "helloworld";
cout << str.size() << endl; // 10
cout << str.length() << endl; // 10
// cout << str[1] << endl; // e
cout << str.at(100) << endl; // e
cout << "helloworld" << endl;
return 0;
}
```
string可以使用for循环将数据取出。
cpp
```cpp
#include <iostream>
using namespace std;
int main()
{
string str = "helloworld";
cout << str.size() << endl; // 10
cout << str.length() << endl; // 10
// cout << str[1] << endl; // e
cout << str.at(100) << endl; // e
cout << "helloworld" << endl;
return 0;
}
```
9.函数重载(掌握)
C++中允许多个函数使用同一个名称,这种用法就是函数重载。函数重载要求函数名称相同,但是参数不同(类型、数量、前后顺序)。与返回值等其他类型无关。
cpp
#include <iostream>
using namespace std;
void print_show(int i)
{
cout << "调用了int1重载:" << i << endl;
}
void print_show(int i,int i2)
{
cout << "调用了int2重载:" << i << i2 << endl;
}
void print_show(double d)
{
cout << "调用了double重载:" << d << endl;
}
void print_show(string s)
{
cout << "调用了string重载:" << s << endl;
}
// 错误,返回值类型,不能作为重载条件
//double print_show(int i)
//{
// cout << "调用了int1重载:" << i << endl;
//}
int main()
{
print_show(1);
return 0;
}
10.类与对象(重点)
10.1 概念
类:类是一个抽象的概念,用于描述一类对象的特点。
对象:根据类的概念所创造的实体。
【思考】:一个对象可以没有对应的类吗?
不可以。
10.2 类的内容
类中最基础的内容包括两个部分,一个是属性,一个是行为。
属性:表示一些特征项的数值,例如:身高、体重、年龄、颜色、型号等等,这些特征的数值一般使用变量进行存储,而类中的变量也被称为"成员变量"。
行为:表示能执行的动作,能做什么事情。例如:吃饭睡觉打游戏等,行为一般通过函数来实现,而类中的函数也被称为"成员函数"。
【例子】以手机为例,来说明类的定义。
规定手机能播放音乐、运行游戏、打电话。手机有品牌、型号、重量属性。
cpp
#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母是大写
class MobilePhone
{
public: // 权限:public公有权限(最开放的权限)
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
// 成员函数(行为)
void play_music()
{
cout << "只因你太美,哒哒哒" << endl;
}
void run_game()
{
cout << "王者荣耀、元神启动、三角洲行动" << endl;
}
void call()
{
cout << "歪歪歪 你好呀" << endl;
}
};
int main()
{
return 0;
}
10.3 对象的创建
在C++中存在两类对象。
栈内存对象
对象所在的{}执行完毕后,自动被销毁
cpp
```cpp
#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母是大写
class MobilePhone
{
public: // 权限:public公有权限(最开放的权限)
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
// 成员函数(行为)
void play_music()
{
cout << "只因你太美,哒哒哒" << endl;
}
void run_game()
{
cout << "王者荣耀、元神启动、三角洲行动" << endl;
}
void call()
{
cout << "歪歪歪 你好呀" << endl;
}
};
int main()
{
MobilePhone mp; // 栈内存对象mp
mp.brand = "小米";
mp.model = "15";
mp.weight = 300;
cout << mp.brand << " " << mp.model << " " << mp.weight << endl;
mp.play_music();
mp.run_game();
mp.call();
return 0;
}
```
堆内存对象
必须使用new关键字创建,使用指针存储,使用delete销毁空间。如果不使用delete销毁,则堆内存对象将会持续存在,从而导致内存泄漏。
cpp
```cpp
#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母是大写
class MobilePhone
{
public: // 权限:public公有权限(最开放的权限)
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
// 成员函数(行为)
void play_music()
{
cout << "只因你太美,哒哒哒" << endl;
}
void run_game()
{
cout << "王者荣耀、元神启动、三角洲行动" << endl;
}
void call()
{
cout << "歪歪歪 你好呀" << endl;
}
};
int main()
{
MobilePhone mp; // 栈内存对象mp
mp.brand = "小米";
mp.model = "15";
mp.weight = 300;
cout << mp.brand << " " << mp.model << " " << mp.weight << endl;
mp.play_music();
mp.run_game();
mp.call();
return 0;
}
```
11.封装(重点)
在上一节中MObilePhone类与结构体差别不大,实际上可以认为结构体就是一种完全开放的类。
封装指的是,将类的一些属性和行为隐藏,重新提供外部的访问接口。封装可以提升代码的安全性,并且可以让程序员更关注上层架构而非内部细节。
cpp
```cpp
#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母是大写
class MobilePhone
{
public: // 权限:public公有权限(最开放的权限)
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
// 成员函数(行为)
void play_music()
{
cout << "只因你太美,哒哒哒" << endl;
}
void run_game()
{
cout << "王者荣耀、元神启动、三角洲行动" << endl;
}
void call()
{
cout << "歪歪歪 你好呀" << endl;
}
};
int main()
{
MobilePhone mp; // 栈内存对象mp
mp.brand = "小米";
mp.model = "15";
mp.weight = 300;
cout << mp.brand << " " << mp.model << " " << mp.weight << endl;
mp.play_music();
mp.run_game();
mp.call();
return 0;
}
```
12.构造函数(重点)
12.1 基本使用
构造函数是一种特殊的成员函数,用于创建对象时初始化。创建对象时必须直接或间接调用当前类的任意一个构造函数,写法上有以下的要求:
- 函数名称必须与类完全相同
- 创建对象时自动调用
- 构造函数没有返回值类型
- 可以进行重载
- 如果程序员不编写构造函数,编译器会自动添加一个无参数的构造函数

手动添加构造函数后,编译器将不会再自动添加默认无构造函数了。
构造函数再创建对象时,常用于给对象的属性赋予初始值
cpp
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 权限:私有权限,也是最封闭权限,只能在类内访问
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
public: // 权限:public公有权限(最开放的权限)
MobilePhone()
{
brand = "小米";
model = "60 pro";
weight = 200;
}
string get_brand()
{
return brand;
}
string get_model()
{
return model;
}
int get_weight()
{
return weight;
}
};
int main()
{
cout << "程序开始执行" << endl;
MobilePhone* mp = new MobilePhone; // 堆内存对象
cout << mp->get_brand() << endl;
cout << mp->get_model() << endl;
cout << mp->get_weight() << endl;
delete mp; // 手动销毁
return 0;
}
构造函数也支持函数重载
cpp
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 权限:私有权限,也是最封闭权限,只能在类内访问
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
public: // 权限:public公有权限(最开放的权限)
MobilePhone()
{
brand = "小米";
model = "60 pro";
weight = 200;
}
// 有参构造函数
MobilePhone(string b, string m,int w)
{
brand = b;
model = m;
weight = w;
}
string get_brand()
{
return brand;
}
string get_model()
{
return model;
}
int get_weight()
{
return weight;
}
};
int main()
{
cout << "程序开始执行" << endl;
MobilePhone* mp = new MobilePhone("8848","钛金手机",500); // 堆内存对象
cout << mp->get_brand() << endl;
cout << mp->get_model() << endl;
cout << mp->get_weight() << endl;
delete mp; // 手动销毁
MobilePhone mp2; // 调用无参构造函数
MobilePhone mp3("vivo","70",300); // 调用有参构造函数
return 0;
}
12.2 构造初始化列表
构造初始化列表是一种更简单的给成员变量赋予初始值的写法。
cpp
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 权限:私有权限,也是最封闭权限,只能在类内访问
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
public: // 权限:public公有权限(最开放的权限)
MobilePhone()
:brand("小米"),model("60 pro"),weight(200)
{
cout << "构造函数被执行了" << endl;
// brand = "华为";
}
// 有参构造函数
MobilePhone(string b, string m,int w)
{
brand = b;
model = m;
weight = w;
}
string get_brand()
{
return brand;
}
string get_model()
{
return model;
}
int get_weight()
{
return weight;
}
};
int main()
{
MobilePhone mp2; // 调用无参构造函数
cout << mp2.get_brand() << endl;
cout << mp2.get_model() << endl;
cout << mp2.get_weight() << endl;
// MobilePhone mp3("vivo","70",300); // 调用有参构造函数
return 0;
}
当构造函数局部变量与局部变量重名时,除了使用后面学习的this指针之外,也可以使用构造初始化列表来区分。

12.3 拷贝构造函数
12.3.1 概念
当程序员不手动编写构造函数时,编译器会自动添加一个拷贝构造函数,使对象的创建可以通过这个构造函数实现。

cpp
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 权限:私有权限,也是最封闭权限,只能在类内访问
// 成员变量(属性)
string brand; // 品牌
string model; // 型号
int weight; // 重量
public: // 权限:public公有权限(最开放的权限)
MobilePhone()
:brand("小米"),model("60 pro"),weight(200)
{
cout << "构造函数被执行了" << endl;
// brand = "华为";
}
// 有参构造函数
MobilePhone(string brand, string model,int weight)
:brand(brand),model(model),weight(weight)
{
}
// 手动编写编译器默认添加的拷贝构造函数
MobilePhone(const MobilePhone &mp)
{
brand = mp.brand;
model = mp.model;
weight = mp.weight;
}
string get_brand()
{
return brand;
}
string get_model()
{
return model;
}
int get_weight()
{
return weight;
}
};
int main()
{
MobilePhone mp1("小米","13",300); // 调用有参构造函数,栈内存对象
MobilePhone mp2(mp1); // 调用拷贝构造函数
cout << mp2.get_brand() << endl;
cout << mp2.get_model() << endl;
cout << mp2.get_weight() << endl;
return 0;
}
12.3.2 浅拷贝
cpp
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
name = n;
}
void show_name()
{
cout << name << endl;
}
};
int main()
{
char arr[20] = "旺财";
Dog d1(arr); // 调用有参构造函数
Dog d2(d1); // 调用拷贝构造函数
strcpy(arr,"大黄");
d1.show_name(); // 大黄
d2.show_name(); // 大黄
return 0;
}

这种情况必须手动编写拷贝构造函数,使每次赋值都创建一个新的副本,从而让每个对象单独持有自己的成员变量。这种方式也被称为"深拷贝"。
12.3.3 深拷贝

cpp
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
name = new char[20];
strcpy(name,n);
}
// 拷贝构造函数
Dog(const Dog &d)
{
name = new char[20];
strcpy(name,d.name);
}
void show_name()
{
cout << name << endl;
}
};
int main()
{
char arr[20] = "旺财";
Dog d1(arr); // 调用有参构造函数
Dog d2(d1); // 调用拷贝构造函数
strcpy(arr,"大黄");
d1.show_name(); // 旺财
d2.show_name(); // 旺财
return 0;
}
13.析构函数(重点)
析构函数是与构造函数对立的函数
|------------|-----------------|
| 构造函数 | 析构函数 |
| 创建对象时手动调用 | 当对象销毁时,自动调用 |
| 函数名称是类名 | 函数名称是~类名 |
| 构造函数可以重载 | 析构函数没有参数,不能进行重载 |
| 用于创建对象时初始化 | 用于销毁对象时释放资源 |
| 没有返回值 | 没有返回值 |
cpp
```cpp
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
name = new char[20];
strcpy(name,n);
}
// 拷贝构造函数
Dog(const Dog &d)
{
name = new char[20];
strcpy(name,d.name);
}
void show_name()
{
cout << name << endl;
}
~Dog()
{
cout << "析构函数被调用了" << endl;
delete []name; // []表示销毁的是连续的空间
}
};
int main()
{
char arr[20] = "旺财";
Dog d1(arr); // 调用有参构造函数
Dog d2(d1); // 调用拷贝构造函数
strcpy(arr,"大黄");
d1.show_name(); // 旺财
d2.show_name(); // 旺财
return 0;
}
```