方法
本课程将向您展示如何利用结构体内部的函数来创建所谓的方法。
动机
在编程中,现实生活中的对象经常被镜像化,例如在创建赛车游戏🏎时,我们希望有由以下内容定义的车辆:
- 特征,例如
- 品牌
- 型号 🚘
- 最高速度 🚀
- 行为,例如
- 加速 💨
- 刹车 🛑
这种描述对象的方式,将特性和行为分开,非常普遍。在关于结构体的课程中,我们学习了如何将对象的不同信息包含在我们自己创建的单一类型中。通过这样做,我们描述了它的特性。现在我们将继续学习方法,它将允许我们“教”对象执行特定任务——也就是说,定义它的行为。
简介
出于本课程的目的,让我们创建一个包含以下汽车特征的结构体
struct Car
{
// car information
std::string brand;
std::string model;
int year_of_production;
// movement
float top_speed = 200; // (km/h)
float acceleration = 50; // (km/h per second)
float speed = 0; // current one (km/h)
};
在main
函数内部,让我们创建此结构体的一个对象
// prism-push-types:Car
int main()
{
Car car;
car.brand = "Ford";
car.model = "Focus";
car.year_of_production = 2010;
}
现在我们将讨论如何使它 (car
) 工作。
创建和使用方法
在Car
结构体内部,在它的末尾,我们放置一个名为accelerate
的函数,它将使speed
增加acceleration
的加速度值
struct Car
{
// ...
float acceleration = 50;
float speed = 0;
// ...
// increases the speed
void accelerate()
{
speed += acceleration;
}
};
方法是结构体(或类——稍后在本课程中介绍)的成员函数。它们操作其他结构体成员和/或为其实例提供一些功能。
现在我们可以像这样在对象上调用accelerate
// prism-push-types:Car
// ...
int main()
{
Car car;
// initial values...
// calling for the first time
car.accelerate();
std::cout << "Current speed: " << car.speed << " km/h\n";
// calling for the second time
car.accelerate();
std::cout << "Current speed: " << car.speed << " km/h\n";
}
Current speed: 50 km/h
Current speed: 100 km/h
我们使用点运算符 (car.accelerate
) 来访问我们想要调用的方法,然后在大括号()
内部,我们根据需要传递任何必需的参数。
object.method_name(arguments);
function_name(arguments);
函数和方法的区别
大多数方法可以重写为常规的自由函数(不在任何结构体之外)。我们上面创建的accelerate
方法可以重写为一个函数
// prism-push-types:Car
void accelerate(Car& car)
{
car.speed += car.acceleration;
}
并这样调用
// prism-push-types:Car
Car car;
// initial values...
// calling for the first time
accelerate(car);
std::cout << "Current speed: " << car.speed << " km/h\n";
// calling for the second time
accelerate(car);
std::cout << "Current speed: " << car.speed << " km/h\n";
查看完整代码
// prism-push-types:Car
#include <iostream>
struct Car
{
// car information
std::string brand;
std::string model;
int year_of_production;
// movement
float top_speed = 200; // (km/h)
float acceleration = 50; // (km/h per second)
float speed = 0; // current one (km/h)
};
void accelerate(Car& car)
{
car.speed += car.acceleration;
}
int main()
{
Car car;
car.brand = "Ford";
car.model = "Mustang";
car.year_of_production = 1969;
car.top_speed = 250;
car.acceleration = 60;
// initial values...
// calling for the first time
accelerate(car);
std::cout << "Current speed: " << car.speed << " km/h\n";
// calling for the second time
accelerate(car);
std::cout << "Current speed: " << car.speed << " km/h\n";
}
如您所见,主要区别在于,在调用函数时需要将对象作为参数传递,但对于方法则不需要这样做。
定义顺序
在结构体内部,方法不必在声明变量或属于此结构体的另一个方法之前定义
- ✔ 好
- ✔ 好(顺序正确)
- ❌ 坏
注意方法的定义顺序:limit_speed
在accelerate
中被使用,即使它的定义在下面。同样,speed
和acceleration
字段在声明之前就被使用了,因为它们在代码中是在这种使用之后。这在结构体内部是允许的。
struct Car
{
// function that increases the speed
void accelerate()
{
speed += acceleration;
limit_speed();
}
void limit_speed() {
if (speed > top_speed)
speed = top_speed;
}
// class data members
float top_speed = 200;
float acceleration = 50;
float speed = 0;
// the rest...
};
函数和变量的顺序是根据它们的用法设置的——同样正确。
struct Car
{
// class data members
float top_speed = 200;
float acceleration = 50;
float speed = 0;
// the rest...
void limit_speed() {
if (speed > top_speed)
speed = top_speed;
}
// function that increases the speed
void accelerate()
{
speed += acceleration;
limit_speed();
}
};
在结构体外部,没有单独声明是不允许这种重排的
void printHelloWorld()
{
// ❌ Error, using `world` function before definition
std::cout << "Hello, " << world();
}
// Return the "World!" string
std::string world()
{
return "World!";
}
声明与定义
与函数一样,我们可以将方法的声明和定义分开。这样我们就可以将它们的定义移到结构体主体之外
struct Car
{
// class data members
float top_speed = 200;
float acceleration = 50;
float speed = 0;
// the rest...
// Methods declarations:
void limit_speed();
void accelerate();
};
void Car::limit_speed()
{
if (speed > top_speed)
speed = top_speed;
}
void Car::accelerate()
{
speed += acceleration;
limit_speed();
}
请注意,在这种情况下,我们在方法名称前加上结构体名称Car
和双冒号::
,即所谓的作用域解析运算符。
// prism-push-types:type
type StructureName::method_name(parameters)
{
// ...
}
这种表示法的优点之一是能够将结构体的接口与其实现分开。这样,一旦实现了方法,每次查看结构体时,您将只看到要使用的变量和方法名称集,而不会被实现细节分散注意力。
当将代码拆分为多个文件时,这种表示法也至关重要——我们将在课程的后面部分进行介绍。
示例
本节需要改进。您可以通过编辑此文档页面来帮助我们。
潜在错误
本节需要改进。您可以通过编辑此文档页面来帮助我们。