c++ Static 成员:让类变量不再“孤单”

开发 前端
静态成员变量和静态成员函数让我们在 C++ 中能更方便地管理共享数据和功能。它的一个关键特点是:它们属于整个类,而不是某个具体的对象。

前言

如果说 static 变量让函数“记住”上一次的状态,那么 static 成员变量就像是整个类的“共享记忆”。这次,我们将从类的角度来聊聊 static 成员变量和函数,帮你彻底搞懂它们是什么,怎么用。

什么是 static 成员变量?

在 C++ 中,类的成员变量通常是属于某个具体对象的,每个对象都会有一份独立的成员变量。而 static 成员变量 可不一样,它是所有对象共享的一个变量。

举个例子:

假设我们有一个类 Car,它有一个成员变量 carCount 用来统计车的数量。

如果每个 Car 对象都单独存储这个数量,那就没什么意义了。因为 carCount 应该是所有 Car 对象共享的,而不是每个对象都自有一份。这个时候,就可以用 static 来声明 carCount。

#include <iostream>
using namespace std;

class Car {
public:
    static int carCount;  // 声明静态成员变量

    Car() {
        carCount++;
    }
};

// 静态成员变量需要在类外定义
int Car::carCount = 0;

int main() {
    Car car1;
    Car car2;

    cout << "Number of cars: " << Car::carCount << endl;  // 输出:2
    return 0;
}

在这个例子中,carCount 变量是静态的,所有 Car 对象共享同一个 carCount,所以无论创建多少个 Car 对象,它都会累加。

如何访问 static 成员?

静态成员的访问和普通成员稍微有些不同。普通成员变量需要通过对象来访问,而静态成员变量可以通过 类名:: 来直接访问,甚至不需要创建对象。

访问静态成员:

class Car {
public:
    static int carCount;  // 静态成员变量

    Car() {
        carCount++;
    }
};

// 在类外定义静态成员
int Car::carCount = 0;

int main() {
    // 直接通过类名访问静态成员
    cout << "Initial car count: " << Car::carCount << endl;  // 输出:0

    Car car1;
    cout << "After one car: " << Car::carCount << endl;  // 输出:1
    Car car2;
    cout << "After two cars: " << Car::carCount << endl;  // 输出:2

    return 0;
}

你可以看到,静态成员 carCount 通过 Car::carCount 来访问,不需要创建 Car 对象。这样就避免了不必要的内存浪费。

静态成员函数

什么是静态成员函数?

静态成员函数 是属于类的,而不是某个对象的。也就是说,你可以通过类名来调用它,而不需要先创建对象。这一点与普通的成员函数不同,普通成员函数是通过对象来调用的。

它有什么特点?

1、类级别的函数:静态成员函数属于类级别,不依赖于任何对象。你不需要创建类的实例,就能通过类名直接调用。

2、只能访问静态成员:静态成员函数只能访问类中的静态成员变量和其他静态成员函数,因为它没有绑定到任何具体的对象,也就无法访问属于对象的非静态成员。

3、没有 this 指针:普通成员函数有一个隐含的 this 指针,指向调用该函数的对象,而静态成员函数没有 this 指针。

与普通成员函数的区别是什么?

  • 普通成员函数:普通成员函数是与对象关联的,它可以访问类的静态和非静态成员。访问非静态成员时,它依赖于对象的 this 指针。
  • 静态成员函数:静态成员函数没有 this 指针,它不能直接访问非静态成员,只能访问静态成员。

举个例子:

#include <iostream>
using namespace std;

class Car {
public:
    static int carCount;  // 静态成员变量

    Car() {
        carCount++;
    }

    // 静态成员函数
    static void printCarCount() {
        cout << "Number of cars: " << carCount << endl;
    }
};

// 静态成员变量需要在类外定义
int Car::carCount = 0;

int main() {
    Car car1;
    Car car2;

    // 通过类名调用静态成员函数
    Car::printCarCount();  // 输出:2
    return 0;
}

在上面的代码中,printCarCount() 是一个静态成员函数,它只能访问静态成员变量 carCount,不能直接访问非静态成员变量。如果尝试访问非静态成员,编译器会报错。

静态成员函数不能访问非静态成员:

class Car {
public:
    int speed;  // 非静态成员变量
    static int carCount;  // 静态成员变量

    static void printSpeed() {
        // 编译错误:静态成员函数无法访问非静态成员变量
        cout << "Speed: " << speed << endl;
    }
};

那为什么静态成员函数不能访问非静态成员?

这个问题其实很好理解,关键在于静态成员函数的“身份”问题。

1、静态成员函数属于类,而不是对象:静态成员函数是在类层面上定义的,它没有绑定到具体的对象。所以,当你调用静态成员函数时,它是通过类名来调用的,不依赖于任何特定的对象。

2、非静态成员属于对象:而非静态成员变量和普通成员函数是属于具体对象的。当你创建一个对象时,非静态成员才会存在,并且只有通过这个对象才能访问这些成员。

静态成员函数无法访问非静态成员的原因就是,它不属于任何特定的对象,所以无法知道该访问哪个对象的非静态成员。换句话说,静态成员函数没有“this”指针,它无法指向具体的对象,也就不能访问属于某个对象的成员。

举个例子来说明:

还是上面 Car 类的例子,speed 是一个非静态成员变量,而 carCount 是一个静态成员变量。

  • 当你调用静态成员函数 printSpeed() 时,它是通过类名来调用的。这个函数没有“this”指针,无法知道是哪个 Car 对象的 speed 变量。
  • 但是,静态成员函数可以访问类中的静态成员 carCount,因为静态成员是属于类的,不依赖于具体的对象。

所以,静态成员函数只能访问静态成员变量和其他静态函数,无法访问非静态成员。

小结:

静态成员函数和对象没关系,它不属于某个具体对象,所以它不能直接操作对象的非静态成员变量。

static 成员的应用场景:

了解了静态成员变量和静态成员函数的基本概念,接下来我们来聊聊它们的实际应用场景。虽然在很多情况下,我们的类对象都有自己独立的成员变量和成员函数,但在某些特定场景下,静态成员就能派上大用场。

1. 全局共享数据

假设我们有一个程序需要统计不同用户的访问次数,而这个次数应该对所有用户共享,而不是每个用户都有一份。这时,静态成员变量就能帮助我们做到这一点。

例如,我们可以在用户类中创建一个静态的访问计数器,所有用户对象共享这个计数器,这样每当有用户访问时,计数器就会增加,而不需要每个对象都单独保存一份。

class User {
public:
    static int visitCount;  // 所有用户共享

    User() {
        visitCount++;
    }
};

// 在类外定义静态变量
int User::visitCount = 0;

int main() {
    User user1;
    User user2;
    cout << "Total visits: " << User::visitCount << endl;  // 输出:2
    return 0;
}

在这个例子中,不管你创建多少个 User 对象,它们都会共享同一个 visitCount,这样就避免了每个用户对象都存储计数的重复工作。

2. 工厂模式中的静态成员

有些时候,类中的静态成员函数可以帮助你创建对象。工厂模式就是一个常见的例子,它允许你通过静态成员函数来创建类的实例,而不需要在外部直接调用构造函数。

class Product {
public:
    static Product* createProduct() {
        return new Product();
    }
};

int main() {
    Product* product = Product::createProduct();
    // 使用 product
    delete product;
    return 0;
}

在这里,createProduct 是一个静态成员函数,用来创建 Product 对象。这种做法能够封装对象创建的细节,提供更灵活的控制。

3. 配置类中的静态成员

在很多程序中,我们可能会有一个配置类,用来保存一些全局的配置数据(如程序的设置、资源路径、日志级别等)。这些配置信息往往是固定的,不会因为对象的创建而变化。此时,静态成员变量非常适合用来保存这些共享的数据。

例如,一个全局的日志配置类可以用静态成员来记录当前的日志级别:

class Logger {
public:
    static int logLevel;

    static void log(const string& message) {
        if (logLevel >= 2) {
            cout << "Log: " << message << endl;
        }
    }
};

// 在类外定义静态变量
int Logger::logLevel = 2;

int main() {
    Logger::log("Program started");
    Logger::logLevel = 1;
    Logger::log("Another log");
    return 0;
}

在这个例子中,logLevel 是一个静态成员,所有日志都根据这个静态设置来决定是否输出。

总结:

静态成员变量和静态成员函数让我们在 C++ 中能更方便地管理共享数据和功能。它的一个关键特点是:它们属于整个类,而不是某个具体的对象。所以,多个对象之间能共享同一份数据,避免了每个对象都要独占一份的情况。这不仅节省内存,也让代码更加简洁高效。

今天我们了解了静态成员变量和静态成员函数的基本用法,知道了如何共享数据、访问静态成员,还明白了为什么静态成员函数不能访问非静态的成员。

责任编辑:武晓燕 来源: 跟着小康学编程
相关推荐

2023-03-21 15:21:52

开发程序设计static

2010-01-18 18:04:28

静态成员

2010-01-18 16:42:13

C++类

2024-02-22 18:07:17

C++静态成员代码

2010-02-01 17:31:06

C++类成员

2011-07-20 16:09:08

C++

2010-01-19 18:35:12

静态成员

2023-10-07 15:53:05

C/C++静态变量内存

2009-05-26 09:31:00

C++重载覆盖

2021-06-18 12:30:36

C++函数指针编程语言

2024-04-08 11:35:34

C++static关键字

2010-02-03 11:01:18

C++类静态成员初始化

2010-01-21 14:19:44

C++静态成员

2023-12-04 10:57:52

函数C++

2024-02-26 10:36:59

C++开发关键字

2011-08-10 17:16:01

Objective-C静态变量

2010-01-13 17:28:32

SOHO交换机

2010-02-03 15:27:26

C++ static

2011-04-07 16:34:05

staticC++

2010-01-26 15:51:06

C++变量
点赞
收藏

51CTO技术栈公众号