一、成员函数的概念
类的成员函数描述的是类的行为,例如 SetTime() 和 ShowTime()。成员函数是程序算法的实现部分,是对封装在类内部的数据进行操作的方法。
与普通函数的区别在于:成员函数属于某个类,它可以直接访问该类的私有成员(private),而非成员函数则无权访问。
二、成员函数的声明与实现分离
成员函数的原型声明写在类体内,描述函数的参数表和返回值类型。函数体的具体实现写在类定义之外。与普通函数不同,在类外实现成员函数时,必须通过作用域限定符 :: 指明所属的类名。
// 类定义(头文件或类定义区)
class Clock
{
private:
int hour, minute, second;
public:
void SetTime(int h, int m, int s); // 原型声明
void ShowTime(); // 原型声明
};
// 类外实现
void Clock::SetTime(int h, int m, int s) // 指明 Clock::
{
if (h >= 0 && h < 24) hour = h;
else hour = 0;
if (m >= 0 && m < 60) minute = m;
else minute = 0;
if (s >= 0 && s < 60) second = s;
else second = 0;
}
void Clock::ShowTime()
{
if (hour < 10) cout << "0";
cout << hour << ":";
if (minute < 10) cout << "0";
cout << minute << ":";
if (second < 10) cout << "0";
cout << second << endl;
}Code language: JavaScript (javascript)
三、成员函数的调用
调用成员函数需要通过 . 运算符(对象直接调用)或 -> 运算符(对象指针调用):
Clock c;
c.SetTime(9, 30, 0); // 对象调用
c.ShowTime();
Clock* p = &c;
p->SetTime(10, 0, 0); // 指针调用
p->ShowTime();Code language: PHP (php)
四、成员函数对私有成员的访问权限
在类的成员函数内部,既可以访问当前对象(this 指向的对象)的私有成员,也可以访问同类其他对象的私有成员。
class Clock
{
private:
int hour, minute, second;
public:
void SetTime(int h, int m, int s)
{
hour = h; minute = m; second = s;
}
// 比较另一个 Clock 对象是否比当前对象时间更早
bool IsEarlierThan(Clock other)
{
// 访问 other 的私有成员——合法!
if (hour < other.hour) return true;
if (hour > other.hour) return false;
if (minute < other.minute) return true;
if (minute > other.minute) return false;
return second < other.second;
}
};Code language: PHP (php)
原理:C++ 的访问控制是基于类的,而不是基于对象的。同一个类的成员函数有权访问该类任何对象的私有成员。
五、带默认形参值的成员函数
类的成员函数也可以有默认形参值,其调用规则与普通函数相同(默认值靠右、同一作用域不重复定义)。关键规则:默认形参值必须写在类定义中(函数原型处),不能写在类外实现中。
class Clock
{
private:
int hour, minute, second;
public:
// 默认值写在类定义中的函数原型里
void SetTime(int h = 0, int m = 0, int s = 0);
void ShowTime();
};
// 类外实现时不能重复写默认值
void Clock::SetTime(int h, int m, int s) // 正确:无默认值
{
hour = h; minute = m; second = s;
}
// void Clock::SetTime(int h = 0, int m = 0, int s = 0) // 错误:默认值不能出现在类外
// {
// hour = h; minute = m; second = s;
// }Code language: JavaScript (javascript)
调用方式:
Clock c;
c.SetTime(); // 使用全部默认值:0:0:0
c.SetTime(9); // h=9, m=0, s=0
c.SetTime(9, 30); // h=9, m=30, s=0
c.SetTime(9, 30, 45); // h=9, m=30, s=45Code language: JavaScript (javascript)
六、内联成员函数
| 方式 | 写法 | 说明 |
|---|---|---|
| 隐式内联 | 函数体直接写在类体内 | 编译器自动视为 inline |
| 显式内联 | 类内声明,类外定义时加 inline 关键字 | 需同时加 inline 和 类名:: |
class Clock
{
public:
// 隐式内联
void ShowTime()
{
cout << hour << ":" << minute << ":" << second << endl;
}
void SetTime(int h, int m, int s); // 非内联声明
};
// 显式内联
inline void Clock::SetTime(int h, int m, int s)
{
hour = h; minute = m; second = s;
}Code language: JavaScript (javascript)
七、代码示例
以下代码综合演示上述所有内容:
#include <iostream>
using namespace std;
class Clock
{
private:
int hour, minute, second;
public:
// 带默认形参值的成员函数(默认值写在类定义中)
void SetTime(int h = 0, int m = 0, int s = 0);
void ShowTime();
// 比较时间先后:访问同类其他对象的私有成员
bool IsEarlierThan(Clock other);
// 隐式内联成员函数
void Reset()
{
hour = 0; minute = 0; second = 0;
}
};
// 类外实现(不带默认值)
void Clock::SetTime(int h, int m, int s)
{
if (h >= 0 && h < 24) hour = h;
else hour = 0;
if (m >= 0 && m < 60) minute = m;
else minute = 0;
if (s >= 0 && s < 60) second = s;
else second = 0;
}
void Clock::ShowTime()
{
if (hour < 10) cout << "0";
cout << hour << ":";
if (minute < 10) cout << "0";
cout << minute << ":";
if (second < 10) cout << "0";
cout << second << endl;
}
bool Clock::IsEarlierThan(Clock other)
{
if (hour < other.hour) return true;
if (hour > other.hour) return false;
if (minute < other.minute) return true;
if (minute > other.minute) return false;
return second < other.second;
}
int main()
{
Clock c1, c2;
// 使用默认参数
c1.SetTime(); // 0:0:0
cout << "c1 时间:";
c1.ShowTime();
c2.SetTime(14, 35, 50);
cout << "c2 时间:";
c2.ShowTime();
// 调用访问同类私有成员的函数
if (c1.IsEarlierThan(c2))
cout << "c1 比 c2 早" << endl;
else
cout << "c1 不比 c2 早" << endl;
// 调用内联成员函数
c1.Reset();
cout << "c1 重置后:";
c1.ShowTime();
return 0;
}Code language: PHP (php)
八、注意事项
- 类外实现成员函数时,函数签名(返回值、函数名、参数类型列表)必须与类内声明完全一致,否则编译器视为重载而非实现。
- 默认形参值只能出现在类定义中的函数原型处,类外实现时不能重复写默认值,否则编译报错。
- 成员函数可以重载(同名不同参数),重载版本各自可以有默认参数,但要注意避免二义性。
- 成员函数内部没有对象前缀时,默认操作当前调用对象(
this指向的对象)。 const成员函数(承诺不修改对象状态)的声明和定义都需要加const,后续章节讲解。
九、常见错误
1. 类外实现时重复写默认参数值
class Clock
{
public:
void SetTime(int h = 0, int m = 0, int s = 0);
};
void Clock::SetTime(int h = 0, int m = 0, int s = 0) // 错误:默认值重复
{
// ...
}Code language: JavaScript (javascript)
2. 类外实现时遗漏类名限定符
void SetTime(int h, int m, int s) // 错误:不是 Clock 的成员
{
hour = h;
}Code language: JavaScript (javascript)
3. 试图从非成员函数访问私有成员
void printHour(Clock c)
{
cout << c.hour; // 错误:hour 是 private
}Code language: JavaScript (javascript)
4. 函数签名不匹配
class Clock
{
public:
void SetTime(int h, int m, int s);
};
void Clock::SetTime(int h, int m) // 错误:参数个数不匹配
{
// ...
}Code language: JavaScript (javascript)
Previous: 58 – 对象