Day 3:类与对象
今天目标:理解 C++ 面向对象的基本思想,掌握类、对象、封装、成员函数、构造函数和成员初始化列表。
1、面向过程和面向对象
面向过程关注的是解决问题的步骤,常见写法是把任务拆成多个函数,按顺序执行。
例如:在开发板屏幕上画矩形:
open 打开屏幕
mmap 内存映射
draw_point 绘制像素点
munmap 解除映射
close 关闭文件
面向对象关注的是解决问题需要哪些对象,把数据和操作数据的函数放到对象内部。
例如:
定义 Screen 类,描述屏幕
创建 Screen 对象
定义 Rect 类,描述矩形
创建 Rect 对象
让 Rect 对象绘制到 Screen 对象上
一句话理解:
面向过程:围绕步骤写代码。
面向对象:围绕对象写代码。
2、面向对象三大特性
C++ 面向对象有三个核心特征:
封装:把数据和函数放到类中,隐藏内部实现,只暴露必要接口。
继承:在已有类基础上扩展新类。
多态:同一个接口,在不同对象上表现出不同效果。
当前阶段先重点掌握封装、类和对象。
3、类和对象
3.1、类 class
类是 C++ 的自定义类型,用来描述一类事物的属性和行为。
属性:成员变量
行为:成员函数
例如坐标点:
class Point
{
public:
void setX(int x);
void setY(int y);
void move(int x, int y);
int getX() const;
int getY() const;
private:
int _x = 0; // x 坐标
int _y = 0; // y 坐标
};
3.2、对象 object
对象就是用类创建出来的具体变量,也叫实例化对象。
Point p1; // 创建一个 Point 对象
Point p2; // 再创建一个 Point 对象
简单理解:
类是图纸,对象是根据图纸造出来的具体东西。
4、封装和访问权限
封装就是把数据和函数放到类里面,并通过访问权限控制外部能不能直接访问。
常见写法:
private:放成员变量,保护数据
public :放成员函数,对外提供接口
访问权限:
public 公有,类外可以访问
private 私有,只能类内访问
protected 保护,类内和子类可以访问
示例:
Point p;
p.setX(100); // 正确,通过 public 函数修改
// p._x = 100; // 错误,_x 是 private,类外不能直接访问
class 和 struct 的区别
class 默认权限是 private
struct 默认权限是 public
实际开发中:
需要封装数据和行为:常用 class
只是简单保存数据:可以用 struct
5、类的声明和定义分离
标准 C++ 中,类一般分成三个文件:
point.hpp 类的声明
point.cpp 类的实现
main.cpp 类的使用
5.1、point.hpp:类的声明
#ifndef POINT_HPP
#define POINT_HPP
class Point
{
public:
void setX(int x);
void setY(int y);
void move(int x, int y);
int getX() const;
int getY() const;
private:
int _x = 0; // x 坐标
int _y = 0; // y 坐标
};
#endif
头文件保护:
#ifndef POINT_HPP
#define POINT_HPP
...
#endif
作用是防止头文件被重复包含。
5.2、point.cpp:类的定义
#include "point.hpp"
void Point::setX(int x)
{
_x = x;
}
void Point::setY(int y)
{
_y = y;
}
void Point::move(int x, int y)
{
_x = x;
_y = y;
}
int Point::getX() const
{
return _x;
}
int Point::getY() const
{
return _y;
}
注意:
void Point::setX(int x)
这里的 Point:: 表示 setX 属于 Point 类。
类外定义成员函数时,必须写:
类名::函数名
5.3、main.cpp:类的使用
#include <iostream>
#include "point.hpp"
void printPoint(const Point& pos)
{
std::cout << "x = " << pos.getX() << std::endl;
std::cout << "y = " << pos.getY() << std::endl;
// pos.setX(100); // 错误,const 引用只能调用 const 成员函数
}
int main()
{
Point pos;
pos.move(100, 200);
printPoint(pos);
return 0;
}
编译:
g++ main.cpp point.cpp -o app
6、只读成员函数 const
如果成员函数只读取数据,不修改成员变量,就应该在函数后面加 const。
声明:
int getX() const;
定义:
int Point::getX() const
{
return _x;
}
作用:
1. 表示这个函数不会修改对象内部数据
2. const 对象或 const 引用只能调用 const 成员函数
3. get 函数通常都写成 const 成员函数
错误示例:
int Point::getX() const
{
_x = 100; // 错误,const 成员函数不能修改成员变量
return _x;
}
7、构造函数
构造函数是类中的特殊成员函数,用来在创建对象时初始化成员变量。
特点:
1. 函数名和类名相同
2. 没有返回类型
3. 创建对象时自动调用
4. 可以重载
5. 如果没有写构造函数,编译器会自动生成默认构造函数
示例:
class Point
{
public:
Point()
{
_x = 0;
_y = 0;
}
Point(int x, int y)
{
_x = x;
_y = y;
}
private:
int _x;
int _y;
};
使用:
Point p1; // 调用无参构造函数
Point p2(100, 200); // 调用有参构造函数
Point p3{1, 2}; // C++11 写法
注意:
Point p4(); // 这不是创建对象,而是声明一个函数
Point p5{}; // 这是创建对象
默认构造函数
默认构造函数指不传参数也能调用的构造函数。
两种常见形式:
Point(); // 无参构造
Point(int x = 0, int y = 0); // 参数都有默认值
匿名对象
直接调用构造函数会产生一个没有名字的临时对象,也叫匿名对象。
foo(Point(100, 200)); // 用临时对象作为函数参数
8、成员初始化列表
成员初始化列表写在构造函数参数列表后面,用冒号 : 开始。
格式:
类名(参数列表) : 成员1(初值), 成员2(初值)
{
}
示例:
class Point
{
public:
Point(int x, int y) : _x(x), _y(y)
{
}
private:
int _x;
int _y;
};
这比在构造函数体里赋值更推荐,因为它是初始化,效率更高。
必须使用成员初始化列表的情况
1. 成员变量是 const 常量
2. 成员变量是引用
3. 成员对象没有默认构造函数
示例:
class Other
{
public:
Other(int value) : _value(value)
{
}
private:
int _value;
};
class Demo
{
public:
Demo(int x, int y) : _x(x), _y(y), _r(_x), _oth(100)
{
}
private:
int _x;
const int _y; // const 成员必须初始化
int& _r; // 引用成员必须初始化
Other _oth; // Other 没有默认构造函数,必须初始化
};
9、总结
1. 类是自定义类型,用来描述一类事物的属性和行为。
2. 对象是由类创建出来的具体变量。
3. 封装是把数据隐藏起来,通过 public 函数安全访问。
4. public 可以类外访问,private 只能类内访问。
5. class 默认 private,struct 默认 public。
6. 类的声明通常写 .hpp,定义通常写 .cpp。
7. 类外定义成员函数要写 类名::函数名。
8. get 函数通常写 const,因为它只读取数据。
9. 构造函数在创建对象时自动调用,用于初始化成员变量。
10. 成员初始化列表更推荐,const 成员、引用成员、无默认构造的成员对象必须用它。
一句话记忆:
类负责描述,对象负责使用;封装负责保护数据,构造函数负责初始化对象。






Comments | NOTHING