1.了解面向过程和面向对象
面向过程的语言,关注的重点是“算法”,可以认为是一系列有序的步骤,只要按照这个步骤来做,就能得到预期的结果,这种思考方式更接近于计算机的思考方式。优点是可以把控到每个细节,因此程序的执行效率高;缺点是开发效率低。
面向对象的编程语言,关注重点是“对象”,可以认为“对象”的本质是一系列由于某种联系聚焦在一起数据单元体。不同单元体之间进行数据交互,从而实现代码运行的结果,程序员只需要站在更高层次去管理这些单元体,这种思考方式更接近人类的思考方式。优点是程序的开发效率高;缺点是执行效率低。
2. 类与对象的概念
类:类是对同一类对象的抽象总结,是一个概念。
对象:按照类的规定创建的实体。
一个类主要包括:属性、行为
属性:用来描述对象的数据元素,通常是一个名词变量,例如:身高、体重、价格等,也称为“成员变量”或“数据成员”。
行为:用来描述对象执行的具体操作,通常对属性进行操作,以动词函数的方式存在,例如:吃饭、睡觉、运行等,也称为“成员函数”或“成员方法”。
成员变量和成员函数统称为“成员”。
3. 类的定义
以手机为例,来说明类的定义。
规定手机的属性:品牌、型号和重量
手机的成员函数:运行游戏、播放音乐、通信
3. 实例化对象
类是一个抽象的概念,因此需要按照这个概念创建对应的对象实体,C++中有两种类型的对象:
栈内存对象:
在生命周期结束(所在的花括号执行完)后,自动被销毁。
栈内存对象使用 . 调用成员。
需要使用new关键字创建,使用delete关键字销毁,如果不销毁,则会持续存在,容易导致内存泄漏的问题,内存泄漏最终可能会导致程序卡顿,甚至卡死。
堆内存对象通常使用指针来保存堆内存对象的地址。
堆内存对象使用 -> 调用成员,在Qt Creator下,直接打.会转换成 ->
5. 封装
封装的写法并不唯一,在实际的过程中会结合业务需求而定,下面的以一个最基础的案例进行编写。通常先对属性私有化,使属性隐藏,然后根据当前属性的需求,通过getter函数和setter函数对类外分别公开读和写的功能。
封装有利于提升程序的安全性。
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 私有权限:只有类内部可访问
string brand; // 可读可写
string model; // 只写
int weight = 188; // 初始值
public: // 公开接口
string get_brand() // getter:读属性
{
return brand;
}
void set_brand(string b) // setter:写属性
{
brand = b;
}
void set_model(string m) // setter
{
model = m;
}
int get_weight() // getter
{
return weight;
}
};
int main()
{
MobilePhone mp1;
mp1.set_brand("小米");
mp1.set_model("13 Pro");
cout << mp1.get_brand() << " " << mp1.get_weight() << endl;
MobilePhone* mp2 = new MobilePhone;
mp2->set_brand("红米");
mp2->set_model("K60 Pro");
cout << mp2->get_brand() << " " << mp2->get_weight() << endl;
delete mp2;
return 0;
}
6. 构造函数
6.1 概念
构造函数类内一种特殊的函数,用来创建一个对象。如果一个类中,程序员不手动编写构造函数,编译器会为这个类自动添加一个无参构造函数,且此函数的函数体为空;如果程序员手写了任意一个构造函数,编译器就不再自动添加构造函数了。
构造函数要求函数名必须与类名完全一致,且构造函数无需写返回值。
6.2 传参
可以给构造函数增加参数,使用参数给属性赋予初始值,使对象的创建更灵活。
6.3 重载
构造函数也支持函数重载,遵守之前函数重载的规则。
6.4 参数默认值
构造函数也支持之前的参数默认值设定。
6.5 构造初始化列表
6.6 拷贝构造函数
如果程序员在一个类中不手动编写拷贝构造函数,编译器会为这个类自动添加一个拷贝构造函数,拷贝构造函数与普通构造函数也是函数重载的关系。
对象和对象之间是独立存在的实体,数据也是相互独立的,拷贝构造函数可以把一个对象的属性值拷贝到新创建的对象中。
7. 析构函数
析构函数是与构造函数对立的函数。
构造函数 | 析构函数 |
手动调用 | 在对象被销毁时自动调用 |
通常用于在对象创建时初始化 | 通常用于在对象销毁时回收资源 |
可以被重载 | 不可以重载,因为没有参数 |
函数名是类名 | 函数名是~类名 |
8. 作用域限定符 ::
8.1 名字空间
std是C++标准库的一个名字空间,很多使用的内容都是来自于标准名字空间,例如字符串std::string、std::cout...
当项目中包含using namespace std;时,代码中使用std名字空间中的内容就可以省略前面的std::
8.2 类内声明,类外定义
对于类中的成员也可以声明定义分离,如果声明定义分离,通常在类内声明,在类外定义,类外的定义需要结合作用域限定符使用。
9. explicit关键字
如果赋值时,刚好赋值运算符右边的数值是左边类的构造函数可以接收的类型,编译器则会自动调用这个构造函数并把赋值运算符右边的数值传入构造函数中,相当于隐式调用了构造函数。
有时候在一些参数传递的过程中,隐式构造可能会在程序员无意的情况下触发了构造函数,因此可以在构造函数前添加explicit关键字修饰,屏蔽隐式调用的用法。
10. this指针
10.1 概念
this指针是一个特殊的指针,保存的是当前类的对象首地址。
实际上可以通过下面的方法判断this的指向:
this所在的函数是哪个对象的,this指向的就是这个对象。
10.2 原理
在类内调用此类的成员,虽然不用手写this指针,但是编译器都会使用this指针来调用成员,因为成员只能由对象来调用,而this指针指向的就是当前类的对象。
10.3 应用
利用this指针的原理,其应用有:
- 区分重名的成员变量与局部变量
- 链式调用
- 多态传参
10.3.1 区分重名变量
当成员变量与局部变量重名时,可以使用this指针调用成员变量。
10.3.2 链式调用
11. static关键字
静态成员变量
静态成员函数
使用static关键字修饰局部变量就是静态局部变量。
静态局部变量所在的函数第一次被调用时,静态局部变量创建,在程序结束时才销毁。
11.2 静态成员变量
成员变量使用static修饰就是静态成员变量,静态成员变量具有以下特点:
- 此类的所有对象共用此变量
- 非const的静态成员变量通常需要类内声明,类外初始化
- 静态成员变量可以直接使用类名::来调用,更推荐使用此方式
- 静态成员变量在程序运行时创建,在程序结束时销毁
11.3 静态成员函数
成员函数使用static修饰就是静态成员函数,静态成员函数的特点有:
- 静态成员函数不能访问此类中非静态成员,因为没有this指针
- 静态成员函数只能调用本类中静态的成员
- 非静态成员函数可以调用静态成员
- 除了可以使用当前类对象调用静态成员函数外,也可以直接使用类名::调用,推荐后者
- 如果静态成员函数声明与定义分离,只需要在声明处使用static修饰
12. const关键字
在C++中,虽然认为const表示常量的意思,但是严格地讲,const并不是常量。因为C++中const只能在程序的运行期间只读,即在编译期可以改变数值。
12.1 常成员函数
const修饰的成员函数,表示常成员函数,这种函数的特点是:
- 可以调用本类中非const的成员变量,但是不能修改其数值
- 不能调用非const的成员函数
建议成员函数只要不修改成员变量值就写为常成员函数,例如getter
const修饰对象,表示该对象为常量对象,其特点有:
- 常量对象的任何属性值不能被修改
- 常量对象不能调用任何非const的成员函数
const修饰对象时,const关键字可以写在类名前面,也可以类名后面。
使用const修饰成员变量,表示该成员变量为常成员变量,其特点有:
- 程序运行时,常成员变量的值不可变
- 不能在函数体中赋值,只能通过直接赋值或构造初始化列表赋值
类似于之前给引用参数增加const修饰,函数的局部变量都可以使用const修饰,表示常量。