C++中非成员函数、静态成员函数和全局函数的理解

1,066 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

一、基本定义

1. 非成员函数

指没有隶属于任何类或结构体的函数。它们可以定义在命名空间中,以提供一些与命名空间相关的功能。

// 在命名空间中定义非成员函数
namespace Calculate {
    int add(int a, int b) {
        return a + b;
    }
}//namespace

// 在另一个文件中使用 MyNamespace 中的 add 函数
#include "calculate.h"
using namespace Calculate;

int main() {
    int sum = add(1, 2); // 调用 Calculate 中的 add 函数
    return 0;
}

比如这里的函数 add 就是一个非成员函数,这种非成员函数可以提供一些与命名空间相关的功能,比如还可以在写一些与计算有关的功能函数subtract,multiplydivide等。这种非成员函数放在命名空间内可避免污染全局作用域,是一种十分规范的写法。

2. 静态成员函数

指属于某个类或结构体的静态函数。它们不需要创建对象实例即可调用,并且只能访问静态成员变量。静态成员函数在命名空间中声明。

class Counter {
private:
    static int count;  // 静态成员变量

public:
    static void increment() {  // 静态成员函数
        count++;
    }

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

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

int main() {
    Counter::increment();  // 直接使用类名调用静态成员函数
    Counter::increment();
    std::cout << Counter::getCount() << std::endl;  // 直接使用类名调用静态成员函数
    return 0;
}

其中 incrementgetCount 就是 Counter 中的静态成员函数;它们不需要创建对象实例即可调用,直接使用类名调用静态成员函数即可,并且只能访问静态成员变量 count,如果还有其他非静态的成员变量,静态成员函数是不能访问的。

3. 全局函数

指定义在命名空间之外的函数。它们可以被任何人访问,并且不需要通过类或结构体来调用。全局函数可以在任何文件中定义和使用,但要使用其他文件中的全局函数,则需要进行声明。

// 在全局作用域中定义一个函数
int add(int a, int b) {
    return a + b;
}

// 在另一个文件中使用全局函数 add
#include <iostream>

int main() {
    int sum = add(1, 2); // 调用全局函数 add
    std::cout << sum << std::endl;
    return 0;
}

二、使用中的注意事项

一起来看一下下面这段代码

#include <iostream>

namespace MyNamespace {
    static int count = 0;  // 声明静态成员变量
    void staticFunc() {  // 声明静态函数
        std::cout << "This is a static function in MyNamespace." << std::endl;
    }
    class MyClass {
    public:
        static void staticMemberFunc() {  // 声明静态成员函数
            count++;  // 访问静态成员变量
            std::cout << "This is a static member function in MyNamespace::MyClass." << std::endl;
        }
    };
}

int main() {
    MyNamespace::staticFunc();  // 调用静态函数
    MyNamespace::MyClass::staticMemberFunc();  // 调用静态成员函数
    return 0;
}

总结:

  • 使用静态成员函数命名空间内的非成员函数,尽量不要用裸的全局函数。这是因为使用静态成员函数或命名空间内的非成员函数可以更好地组织代码,使得每个函数都有其独立的作用域,并且避免了命名冲突。

  • 将一系列函数直接置于命名空间中,不要用类的静态方法模拟出命名空间的效果,类的静态方法应当和类的实例或静态数据紧密相关。比如下面的代码 MyNamespace::MyUtils::add(1, 2) 就是不可取的。

    #include <iostream>
    
    class MyNamespace {
    public:
        class MyUtils {  // 模拟命名空间中的类
        public:
            static int add(int x, int y) {
                return x + y;
            }
        };
    };
    
    int main() {
        int sum = MyNamespace::MyUtils::add(1, 2);  // 使用静态方法模拟命名空间
        std::cout << sum << std::endl;
        return 0;
    }
    
  • 有时, 把函数的定义同类的实例脱钩是有益的, 甚至是必要的. 这样的函数可以被定义成静态成员, 或是非成员函数. 非成员函数不应依赖于外部变量, 应尽量置于某个命名空间内. 相比单纯为了封装若干不共享任何静态数据的静态成员函数而创建类, 不如使用命名空间。 比如应该这样写

    namespace myproject {
    namespace foo_bar {
    void Function1();
    void Function2();
    }  // namespace foo_bar
    }  // namespace myproject
    

    而不是

    namespace myproject {
    class FooBar {
     public:
      static void Function1();
      static void Function2();
    };
    }  // namespace myproject