简介
类
现在开始学习 C++ 的核心编程范式之一:面向对象编程,简称 OOP。它以类为中心;类是用户定义的数据类型,包含一组相关函数。本节从基础开始,后续课程会介绍进阶主题。
成员
类可以拥有成员变量和成员函数,通过成员选择运算符 . 访问。和类外变量一样,建议在声明成员变量时赋予初始值。这个值将作为该类新对象的默认值。
封装与信息隐藏
类可以限制对成员的访问。两个基本访问说明符是 private 和 public。私有成员不能从类外访问,公开成员可以自由访问。class 的成员默认都是 private,只有明确标记为 public 的成员才能从类外直接使用。
基本示例
下面展示了类的定义方式,注意定义结束后的分号 ;:
class Wizard {
public: // from here on all members are publicly accessible
int cast_spell() { // defines the public member function cast_spell
return damage;
}
std::string name{}; // defines the public member variable `name`
private: // from here on all members are private
int damage{5}; // defines the private member variable `damage`
};
在类内可以访问所有成员变量,请观察 cast_spell 中对 damage 的使用。类外不能直接读取或修改私有成员:
Wizard silverhand{};
// calling the `cast_spell` function is okay, it is public:
silverhand.cast_spell();
// => 5
// name is public and can be changed:
silverhand.name = "Laeral";
// damage is private:
silverhand.damage = 500;
// => Compilation error
构造函数
构造函数用于在对象创建时给成员变量赋值。它与类同名,没有返回类型。一个类可以有多个构造函数,便于选择性设置变量。例如,有时保留其他默认值、只修改 name;对于更强的巫师,也可能同时修改伤害值,此时可以提供两个构造函数。
class Wizard {
public:
Wizard(std::string new_name) {
name = new_name;
}
Wizard(std::string new_name, int new_damage) {
name = new_name;
damage = new_damage;
}
int cast_spell() {
return damage;
}
std::string name{};
private:
int damage{5};
};
Wizard el{"Eleven"}; // deals 5 damage
Wizard vecna{"Vecna", 50}; // deals 50 damage
构造函数有许多细节。如果没有为类显式定义任何构造函数,编译器通常会提供默认构造函数;上面的第一个示例就是这种情况。silverhand 对象通过不传参数调用默认构造函数来创建,成员采用类定义中指定的初始值。如果没有提供初始值,某些变量可能未初始化,从而产生意外后果。
补充:结构体
结构体来自 C++ 的 C 语言基础,与 C++ 一样历史悠久。它与类基本相同,但有一个重要区别:class 的成员默认私有,struct 的成员默认公开。习惯上,struct 常用于仅保存数据的结构,class 则常用于需要维持某些性质的对象。例如,Wizard 的 damage 不能为负数就是一种不变条件。把 damage 设为私有,并让所有修改伤害的函数都保证这一条件,就能维护该性质。
任务说明
艾伦正在制作一个玩家与外星人战斗的游戏。她刚学会面向对象编程,迫不及待地想利用类来改进程序。
你提出帮助她,她很高兴,并请你编写玩家将要面对的外星人。
1. 创建 Alien 类
定义 Alien 类,构造函数接收两个 int 参数 x、y,分别保存到成员变量 x_coordinate 和 y_coordinate。每个外星人的初始生命值都是 3,因此也要初始化成员变量 health。
health 应是私有成员。为让程序其他部分读取生命值,提供返回 int 的公开方法 get_health()。
Alien alien{2, 0};
alien.x_coordinate;
// => 2
alien.y_coordinate;
// => 0
alien.get_health();
// => 3
现在,每个外星人都应能保存自己的位置和生命值。
2. hit 方法
为 Alien 添加 hit 方法,每次调用使生命值减 1。这样艾伦可以直接调用 some_alien_instance.hit(),而不必手动修改生命值。生命值不能小于 0。目前该方法应返回 true;艾伦计划以后增加护盾,届时护盾生效时将返回 false。
Alien alien {0, 0};
alien.get_health();
// => 3 (Initial health value)
alien.hit(); // Decrements health by 1 point.
alien.get_health();
// => 2
3. is_alive 方法
随着生命值不断减少,它最终可能变为 0。添加 is_alive 方法,方便艾伦快速判断外星人是否存活。some_alien_instance.is_alive() 应返回布尔值。
alien.get_health();
// => 1
alien.is_alive();
// => true
alien.hit();
alien.get_health();
// => 0
alien.is_alive();
// => false
4. teleport 方法
游戏中的外星人可以瞬间移动!编写 teleport 方法,接收 x_new、y_new 并相应更新坐标。目前返回 true。艾伦计划在后续关卡加入阻止传送的炸弹,届时传送失败将返回 false。
alien.teleport(5, -4);
alien.x_coordinate;
// => 5
alien.y_coordinate;
// => -4
5. collision_detection 方法
既然外星人可能被击中,就需要检测碰撞。艾伦需要判断两个外星人是否占据相同坐标。collision_detection() 接收另一个外星人对象,返回 bool。
Alien lrrr {3, 6};
Alien ndnd {-2, 12};
lrrr.collision_detection(ndnd);
// => false
ndnd.teleport(3, 6);
ndnd.collision_detection(lrrr);
// => true