C++学习笔记----8、掌握类与对象(二)---- 成员函数的更多知识(1)
c++提供了成员函数的更多选择,我们来进行更多解密
1、静态成员函数
成员函数,与数据成员一样,有时候整体应用于类,而不是每个对象。可以写与数据成员一样的静态成员函数。举个例子,考虑上一章的SpreadsheetCell类。有两个辅助函数:stringToDouble()与doubleToString()。这些成员函数不访问特定对象的信息,所以它们可以是静态的。下面是带有静态成员函数的类定义:
export class SpreadsheetCell
{
// Omitted for brevity
private:
static std::string doubleToString(double value);
static double stringToDouble(std::string_view value);
// Omitted for brevity
};
这两个成员函数的实现与前面的实现一样。不需要在成员函数定义前面再重复static关键字。注意静态成员函数关不在某个特定的对象上进行调用,所以它们没有shis指针,不会访问非静态成员进行执行。实际上,一个静态成员函数就像一个通常的函数。唯一的不同就是它可以访问private static和protected static成员。还有,它也可以访问对象上的同样类型的private与protected非静态成员。如果这些对象对静态成员函数可见,例如,通过传递引用或者作为参数指向这样的一个对象的指针。
像一个通常的成员函数那样在类的任何成员函数中调用静态成员函数。这样,在SpreadsheetCell中的所有的成员函数的实现可以保持不变。
在类之外,需要用类名来赋权给静态成员函数名,使用范围解析符。像通常一样应用访问控制。例如,如果有一个带有叫做bar()的公共静态成员函数,可以在下面代码的任何地方调用bar():
Foo::bar();
注意:例子中作为private static定义的doubleToString()与stringToDouble()成员函数只是为了演示怎么定义与使用静态成员函数。对于这样的特定场景,两个成员函数都不会访问重写SpreadsheetCell实例中的任何数据。这样的话,也可以把这些辅助函数定义在SpreadsheetCell类之外spreadsheet_cell模块实现的一个非命名空间。
2、常量成员函数
常量对象就是其值不能改变的对象。如果有一个const,const引用,或者指向const对象的指针,编译器是不会让你调用该对象上的任何成员函数,除非成员函数保证它们不会改变任何数据成员。保证成员函数不会改变数据成员的方式就是用const关键字标记成员函数自身。在上一章开发SpreadsheetCell类的过程中已经完成。提醒一下,下面是用const标记带有不会改变任何成员数据的成员函数的SpreadsheetCell类的一部分:
export class SpreadsheetCell
{
public:
double getValue() const;
std::string getString() const;
// Omitted for brevity
};
const规格是成员函数原型的一部分,必须在定义中也保持:
double SpreadsheetCell::getValue() const
{
return m_value;
}
string SpreadsheetCell::getString() const
{
return doubleToString(m_value);
}
用const标记一个成员函数相当于与客户端代码签署了一个你不会在成员函数中改变对象的内部值的一个合约。如果你声明了一个成员函数const而实际上修改了数据成员,编译器就会报错。const成员函数通过使其在对每一个数据成员拥有reference-to-const的成员函数中出现来工作。这样的话,如果尝试改变一个数据成员,编译器就会报错。
不可以声明静态成员变量为const,因为这样就重复了。静态成员函数在特定的类的实例上不管用,所以它不可能改变内部值。
可以在非常量对象上调用const与non-const成员函数。然而,只能在常量对象上调用常量成员函数。下面举例:
int main()
{
SpreadsheetCell myCell { 5 };
println("{}", myCell.getValue()); // OK
myCell.setString("6"); // OK
const SpreadsheetCell& myCellConstRef { myCell };
println("{}", myCellConstRef.getValue()); // OK
myCellConstRef.setString("6"); // Compilation Error!
}
应该养成把不修改对象的成员函数声明成const的习惯,这样就可以在程序中使用对常量对象的引用。
注意常量对象仍然可以被破坏,可以调用其析构函数。然而,析构函数不允许声明成const。
2.1、可变的数据成员
有时候你写了一个成员函数,“逻辑上”const但是碰巧改变了对象的一个数据成员。这种改变对任何用户可见的数据没有影响,但是技术上确实是一个改变,所以编译器是不会让你声明该成员函数为const的。例如,你想记录你的spreadsheet应用包含数据被阅读的频率。简单粗暴的方式就是给SpreadsheetCell类添加一个计数器记录每次对getValue()或者getString()的调用。不幸的是,在编译器看来,这就使得那些成员函数变成了non-const,这可不是你想要的。解决方案是使你的新计数器变量为可变的,它会告诉编译器在一个const成员函数中这种修改是没有问题的。下面是新的SpreadsheetCell类定义:
export class SpreadsheetCell
{
// Omitted for brevity
private:
double m_value { 0 };
mutable unsigned m_numAccesses { 0 };
};
下面是getValue()与getString()的定义:
double SpreadsheetCell::getValue() const
{
++m_numAccesses;
return m_value;
}
string SpreadsheetCell::getString() const
{
++m_numAccesses;
return doubleToString(m_value);
}