最初我把m_damage作为基类Projectile中的一个类成员,在每个派生类的初始化列表中赋值,并在基类中设置一个getter函数。
但后来我意识到我不需要每个派生类的实例都持有一个m_damage的副本,因为它对每个派生类的所有实例都是相同的值,所以我需要让这个值成为 static
并让每个派生类以 virtual
函数覆盖。
经过网上和这里的研究,我相信 inline static const
变量是最好的方法。但是把它作为类成员或类常量有什么好处吗,还是有其他更好的方法?我只想通过projectile中的私有虚拟函数来访问这个值。
Projectile.h文件
class Projectile
{
private:
virtual int getDamage() const = 0;
}
火箭.h文件
class Rocket : public Projectile
{
private:
// inline static const auto ROCKET_DAMAGE = 400; <---- make it a class member?
virtual int getDamage() const final override;
}
火箭队
// inline static const auto ROCKET_DAMAGE = 400; <---- make it a class constant?
int Rocket::getDamage() const
{
return ROCKET_DAMAGE;
}
从API设计的角度来看,一个常量是一个实现细节。因此,你可能想把这个实现细节--即常量--隐藏在成员函数的定义后面,在基类的 .cpp
文件,因为你已经有了。
在 rocket.hpp
:
class Rocket: public Projectile {
private:
virtual int getDamage() const final override;
};
在 rocket.cpp
:
static constexpr auto ROCKET_DAMAGE = 400; // not exposed to client
int Rocket::getDamage() const {
return ROCKET_DAMAGE;
}
注意到有了常数 ROCKET_DAMAGE
作为数据成员,就像下面的例子一样,将其暴露给客户端。
class Rocket: public Projectile {
private:
inline static const auto ROCKET_DAMAGE = 400;
};
常量可以被编译到客户端的代码中。这意味着每次改变常量的值时,客户端的代码都需要重新编译。ROCKET_DAMAGE
.
相反,如果常数 ROCKET_DAMAGE
不会通过头文件暴露给客户端,因为它被隐藏在了 .cpp
文件(如前一种情况),改变常量的值不可能需要重新编译客户端代码。
这是静态类变量的一个很好的用例。如果是常量,对类的所有对象都是一样的值,如果是数据成员,只会浪费内存。我会继续把它也变成 constexpr
所以它在编译时就被分配了,如果你需要的话,它可以用于编译时的计算(假设你有一个支持C++17的编译器}。
constexpr static auto ROCKET_DAMAGE = 400;
解决这个问题的另一种方法是不使用静态变量,而是直接从虚拟函数中返回值。
int Rocket::getDamage() const
{
return 400;
}
使用静态类变量会使你在将来需要改变值时稍微容易一些:直接修改头文件就可以了,而不是在实现文件中翻找正确的函数定义。然而,这可能是一个负面的因素,如果头文件是 #include
-d在多个文件中,因为这将需要重新编译所有带这个头的源文件,这意味着更长的编译时间。