C++ 面向对象_2

C++ 成员函数

在 C++ 中,成员函数(Member Function)是定义在类(class)或结构(struct)内部的函数,它属于该类的成员。成员函数可以直接访问类的所有成员,包括私有(private)、保护(protected)和公有(public)的成员变量和函数。成员函数通常用来操作类的对象数据或提供类的行为接口。

简单来说,成员函数是类的一部分,与类的对象绑定在一起,描述了对象能够执行的操作。例如,在一个表示“汽车”的类中,成员函数可能是“启动引擎”或“加速”。

为什么需要成员函数?

基于面向对象的设计思想,对象具有的的行为可以看作是与对象深度绑定的函数组成的,因此成员函数也可以称为成员行为或成员方法,成员函数体现了面向对象封装的特点,是面向对象编程中封装的重要组成部分。通过成员函数,类可以将数据(成员变量)与操作这些数据的行为(成员函数)绑定在一起,避免外部直接访问数据,从而保护数据的完整性。

成员函数定义了对象可以做什么。例如,一个“人”类可能有“走路”或“说话”的成员函数,反映现实世界中对象的实际行为。并且可以访问类的私有成员,而外部函数无法直接做到这一点,这提供了更高的灵活性和安全性。

如何使用成员函数?

成员函数的定义和使用分为以下几个步骤:

声明成员函数:在类中声明函数,通常在类的头文件中。
定义成员函数:在类外或类内提供函数的具体实现。
调用成员函数:通过类的对象或指针使用点号(.)或箭头(->)运算符调用。

声明和定义成员函数的示例如下:

#include <iostream>
using namespace std;

class Car {
public:
    int speed; // 成员变量

    // 成员函数声明
    void accelerate(int increment);
    void displaySpeed();
};

// 成员函数定义(类外实现)
void Car::accelerate(int increment) {
    speed += increment;
}

void Car::displaySpeed() {
    cout << "Current speed: " << speed << " km/h" << endl;
}

int main() {
    Car myCar;         // 创建对象
    myCar.speed = 0;   // 初始化成员变量
    myCar.accelerate(50); // 调用成员函数
    myCar.displaySpeed(); // 调用成员函数
    return 0;
}

C++ 中成员函数的定义方式

  1. 在类内部定义:

    直接在类中编写函数实现,适合简单函数。

    class Car {
    public:
        int speed = 0;
        void accelerate(int increment) {
            speed += increment;
        }
    };
    
  2. 在类外部定义:

    在类中声明函数,在类外使用作用域解析运算符(::)定义函数,适合复杂函数。

    class Car {
    public:
        int speed;
        void accelerate(int increment); // 声明
    };
    
    void Car::accelerate(int increment) {
        speed += increment;
    }
    
  3. 内联函数:

    使用 inline 关键字或在类内定义(隐式内联),可以减少函数调用的开销。

    class Car {
    public:
        int speed = 0;
        inline void accelerate(int increment) {
            speed += increment;
        }
    };
    
  4. 静态成员函数

    使用 static 关键字定义,不依赖于对象实例,只能访问类的静态成员。

    class Car {
    public:
        static int totalCars;
        static void addCar() {
            totalCars++;
        }
    };
    int Car::totalCars = 0;
    

成员函数与 this 指针

在每个非静态成员函数中,C++ 隐式传递一个指向调用成员函数的对象的指针,称为 this 指针。
this 是一个常量指针(T* const this),指向当前对象并且表明 this 不可修改指向的对象只能指向调用成员函数的对象,允许成员函数访问对象的成员变量和函数。
当函数参数名与成员变量名冲突时,可以用 this-> 明确指定成员变量。

成员函数可以通过在其声明后添加 const 关键字,成为常量成员函数,表示该函数不会修改对象的成员变量。
const 修饰的成员函数与非 const 版本可以形成重载,编译器根据对象的常量性选择调用哪个版本。
在 const 成员函数中,this 的类型变为 const T* const,限制了对非 mutable 成员变量的修改。

示例如下:

#include <iostream>
using namespace std;

class MyClass {
private:
    int value;

public:
    MyClass(int v) : value(v) {}

    // 非 const 成员函数
    void setValue(int value) {
        this->value = value; // 使用 this 区分参数和成员变量
    }

    // const 成员函数
    int getValue() const {
        return value; // 不会修改对象
    }

    // 非 const 重载
    void display() {
        cout << "Non-const version: " << value << endl;
    }

    // const 重载
    void display() const {
        cout << "Const version: " << value << endl;
    }
};

int main() {
    MyClass obj(10);
    const MyClass constObj(20);

    obj.setValue(15);
    cout << "obj value: " << obj.getValue() << endl; // 调用非 const 对象
    obj.display(); // 调用非 const 版本

    cout << "constObj value: " << constObj.getValue() << endl; // 调用 const 对象
    constObj.display(); // 调用 const 版本

    return 0;
}

成员函数的名称查找与隐藏关系

在函数作用域内,局部变量或形参会隐藏外部同名的变量或函数。对于成员函数,形参名可能会隐藏类的成员变量,因此可以使用 this 指针显示区分成员变量和形参。

类中定义的名称(成员变量或函数)会隐藏外部作用域(如全局作用域)的同名实体。
如果需要访问外部名称,可以使用作用域解析运算符 ::

示例如下:

#include <iostream>
using namespace std;

int x = 100; // 全局变量

class Test {
private:
    int x = 10; // 类成员隐藏全局变量

public:
    void setX(int x) { // 形参隐藏成员变量
        this->x = x; // 使用 this 访问成员变量
    }

    void print() {
        cout << "Member x: " << x << endl;
        cout << "Global x: " << ::x << endl; // 使用 :: 访问全局变量
    }
};

template <typename T>
class TemplateTest {
private:
    T value;

public:
    void setValue(T v) {
        this->value = v; // 依赖型名称查找,必须用 this->
    }

    T getValue() { return value; }
};

int main() {
    Test t;
    t.setX(20);
    t.print();

    TemplateTest<int> tt;
    tt.setValue(30);
    cout << "Template value: " << tt.getValue() << endl;

    return 0;
}

静态成员函数

使用 static 关键字定义静态成员函数,静态成员函数不依赖于对象实例,没有隐式的 this 指针,只能访问类的静态成员(变量或函数)。可以通过类名直接调用,也可以通过对象调用。

示例如下:

#include <iostream>
using namespace std;

class Counter {
private:
    static int count; // 静态数据成员

public:
    Counter() { count++; } // 构造函数增加计数

    static int getCount() { // 静态成员函数
        return count; // 返回静态数据成员
    }
};

int Counter::count = 0; // 静态成员初始化

int main() {
    Counter c1, c2, c3;
    cout << "Total objects: " << Counter::getCount() << endl; // 通过类名调用
    cout << "Total objects: " << c1.getCount() << endl; // 通过对象调用

    return 0;
}

成员函数基于引用限定符的重载

C++11 引入了引用限定符(& 和 &&),用于限制成员函数的调用对象是左值(lvalue)还是右值(rvalue)。
& 表示函数只能被左值对象调用,&& 表示只能被右值对象调用。
可以与 const 结合使用,形成更细粒度的重载。

示例如下:

#include <iostream>
using namespace std;

class Example {
private:
    int data;

public:
    Example(int d) : data(d) {}

    // 左值版本
    void display() & {
        cout << "Lvalue: " << data << endl;
    }

    // 右值版本
    void display() && {
        cout << "Rvalue: " << data << endl;
    }

    // const 左值版本
    void display() const & {
        cout << "Const Lvalue: " << data << endl;
    }
};

int main() {
    Example e1(10);
    e1.display(); // 调用左值版本

    Example(20).display(); // 调用右值版本

    const Example e2(30);
    e2.display(); // 调用 const 左值版本

    return 0;
}