类的成员函数

一、成员函数的概念

类的成员函数描述的是类的行为,例如 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:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注