14-6 访问函数
在之前的第14.5节——公共与私有成员及访问限定符中,我们讨论了公共和私有访问级别。需要提醒的是,类通常将数据成员设为私有,而私有成员无法被公共直接访问。
请看以下Date类:
#include <iostream>
class Date
{
private:
int m_year{ 2020 };
int m_month{ 10 };
int m_day{ 14 };
public:
void print() const
{
std::cout << m_year << '/' << m_month << '/' << m_day << '\n';
}
};
int main()
{
Date d{}; // create a Date object
d.print(); // print the date
return 0;
}
虽然该类提供了用于打印完整日期的print()成员函数,但这可能无法满足用户的需求。例如,当Date对象的用户需要获取年份时该怎么办?或者需要将年份修改为其他值时又该如何?由于m_year是私有成员(因此无法被公共成员直接访问),用户将无法实现这些操作。
对于某些类而言,在符合类功能逻辑的前提下,允许获取或设置私有成员变量的值是合理的。
访问函数
访问函数是一种简单的公共成员函数,其作用是获取或修改私有成员变量的值。
访问函数分为两种类型:获取器和设置器。获取器Getters(有时也称为访问器accessors)是返回私有成员变量值的公共成员函数。设置器Setters(有时也称为修改器mutators)是设置私有成员变量值的公共成员函数。
术语说明
Nomenclature:
“修改器”一词常与“设置器”互换使用。但广义而言,修改器指任何能修改modifies(修改mutates)对象状态的成员函数。按此定义,设置器是修改器的一种特殊类型。但非设置器函数也可能属于修改器范畴。
获取器通常声明为 const,以便在 const 和非 const 对象上均可调用。设置器应声明为 non-const,以便修改数据成员。
为便于说明,我们更新 Date 类使其具备完整的获取器和设置器:
#include <iostream>
class Date
{
private:
int m_year { 2020 };
int m_month { 10 };
int m_day { 14 };
public:
void print()
{
std::cout << m_year << '/' << m_month << '/' << m_day << '\n';
}
int getYear() const { return m_year; } // getter for year
void setYear(int year) { m_year = year; } // setter for year
int getMonth() const { return m_month; } // getter for month
void setMonth(int month) { m_month = month; } // setter for month
int getDay() const { return m_day; } // getter for day
void setDay(int day) { m_day = day; } // setter for day
};
int main()
{
Date d{};
d.setYear(2021);
std::cout << "The year is: " << d.getYear() << '\n';
return 0;
}
这打印:

访问函数命名
访问函数的命名并无统一规范。不过,某些命名约定比其他约定更受欢迎。
- 以“get”和“set”为前缀:
int getDay() const { return m_day; } // getter
void setDay(int day) { m_day = day; } // setter
使用“get”和“set”前缀的优势在于,它明确表明这些是访问函数(且调用成本应较低)。
- 无前缀:
int day() const { return m_day; } // getter
void day(int day) { m_day = day; } // setter
这种风格更为简洁,获取器和设置器使用相同名称(依靠函数重载来区分二者)。C++标准库采用此约定。
无前缀约定的缺点在于,难以直观识别这是在设置day成员的值:
d.day(5); // does this look like it's setting the day member to 5?
关键洞察:
在私有数据成员前添加“m_”前缀的最佳理由之一,是避免数据成员与获取器出现同名情况(C++不支持这种情况,但Java等其他语言支持)。
- 仅限“set”前缀:
int day() const { return m_day; } // getter
void setDay(int day) { m_day = day; } // setter
上述选择取决于个人偏好。但我们强烈建议为设置器使用“set”前缀,获取器可使用“get”前缀或不加前缀。
提示:
为设置器添加“set”前缀,可更清晰地表明其正在改变对象状态。
获取器应通过值或const左值引用返回
获取器应提供数据的“只读”访问权限。因此最佳实践是:若成员复制成本低则按值返回,若成员复制成本高则通过const左值引用返回。
由于通过引用返回数据成员涉及复杂议题,我们将在第14.7课——返回数据成员引用的成员函 中详细探讨该主题。
访问函数的考量
关于何时应使用或避免使用访问函数,业界存在诸多讨论。许多开发者认为使用访问函数违背了良好的类设计原则(这个话题足以写成整本书)。
目前我们建议采取务实策略。创建类时请考虑以下要点:
- 若类无不变性且需大量访问函数,可考虑改用结构体(其数据成员设为public),直接提供成员访问权限。
- 优先实现行为或操作而非访问函数。例如,替代setAlive(bool)设置器,可实现kill()和revive()函数。
- 仅在用户确实需要获取/设置单个成员值时才提供访问函数。
既然要提供公共访问函数,为何还要将数据设为私有?
问得好!这个问题将在第14.8课——数据隐藏(封装)的优势中解答。

浙公网安备 33010602011771号