c++关键字static

本文详细解释了C++中静态成员变量和函数的概念,包括它们的访问权限、生命周期、初始化位置以及在类设计中的应用场景,如单例模式的实现。
摘要由CSDN通过智能技术生成

1. 静态成员变量和函数并不依赖于类的任何对象,它们是类的属性,因此不需要通过类的对象访问,可以通过类名直接访问。

2. 静态成员变量和函数是公有的,可被所有对象访问,但是无法访问类的非静态成员。

3. 静态成员变量和函数不能被声明为constvolatile或者constexpr

4. 静态成员变量必须在类外进行定义,且只能定义一次。

5. 静态成员函数不能直接访问类的非静态成员,静态成员函数及其参数中不允许使用this指针,因为this指针只有实例化后才会产生。

6. 静态成员函数不能被声明为虚函数,因为虚函数实现需要运行时的信息,而静态成员函数不依赖于类的对象,无法确定运行时的信息。

7. 任何需要依靠对象的成员和成员函数都不能定义为静态类型

C++中的static关键字可以用于声明静态成员变量(或数据成员)和静态成员函数。它们与类相关联,而不是与类的对象关联,因此可以被所有类的对象共享。通过使用关键字`static`在类中声明静态数据成员,可以将数据成员与类的对象解耦。它们只有一个实例,并且可以在类的任何成员函数中修改,从而可以用来记录该类的所有对象的统一信息。

// 静态数据成员
#include <iostream>

class player {
public:
    player() {
        ++count;
    }
    // 通过使用关键字`static`在类中声明静态数据成员,可以将数据成员与类的对象解耦。
    // 它们只有一个实例,并且可以在类的任何成员函数中修改,从而可以用来记录该类的所有对象的统一信息。
    static int count;
private:
    int id;
};
// 类的静态成员变量需要在类外进行定义
int player::count = 0;

int main()
{
    player p1, p2, p3;
    std::cout << "total players: " << player::count << std::endl;
    return 0;
}
// 静态成员函数
// 静态成员函数可以使用与静态数据成员相同的语法进行声明:将`static`关键字放在函数的声明之前。
// 与非静态成员函数相比,它们没有this指针,并且只能对静态数据成员进行操作。
#include <iostream>

class car {
public:
    car() {
        ++count;
    }
    static int getCount() {
        return count;
    }
private:
    static int count;
};

int car::count = 0;

int main()
{
    car c1, c2, c3;
    std::cout << "number of cars : " << car::getCount() << std::endl;
    return 0;
}

// 使用静态成员变量实现单例模式
#include <iostream>

class Singleton {
public:
    // 静态成员函数`getInstance()`返回`Singleton`类的单例实例。
    // getInstance()成员函数是一个静态成员函数,它只能访问静态成员变量,因此可以在类内定义,也可以在类外定义。
    static Singleton &getInstance() {
        // instance成员变量是一个静态局部变量,局部静态变量在第一次调用时被初始化,并一直存在直到程序结束,因此不需要在类外进行定义。
        static Singleton instance;
        // 返回一个静态局部变量instance的引用,从而实现了全局唯一的实例。
        // 由于instance是静态局部变量,它只会被初始化一次,并一直存在直到程序结束,
        // 因此Singleton类的使用者可以多次调用getInstance()来获得同一个实例,并且该实例的生命周期与程序的生命周期相同。
        return instance;
    }

    void foo() {
        std::cout << "hello world!" << std::endl;
    }
private:
    // 类中声明了一个私有的无参构造函数,使得该类不能被直接实例化。
    Singleton() {}
    ~Singleton() {}
};

int main()
{
    Singleton &s1 = Singleton::getInstance();
    Singleton &s2 = Singleton::getInstance();

    std::cout << (&s1 == &s2) << std::endl; // true
    // 通过对`s1`和`s2`取地址后,对返回的指针进行解引用,并调用其中的`foo()`函数。
    // 在C++中,对指针进行解引用可以使用`*`或者`->`符号。
    // `*`符号用在对指针变量本身进行解引用时,而`->`符号则用在对指向对象的指针进行解引用时。
    // 因此,`(&s1)->foo()`和`s1.foo()`实际上是等价的。
    // 对`s1`和`s2`取地址后,返回的是指向`Singleton`对象的指针。
    // 因为`foo()`函数是`public`成员函数,可以通过对象的指针调用。
    // 因此,在这里可以对指向`Singleton`对象的指针进行解引用,并调用其中的`foo()`函数。
    (&s1)->foo();
    (&s2)->foo();
    // 打印`s1`和`s2`的指针地址,通过输出结果可以发现,
    // `s1`和`s2`的指针地址是相同的,这说明它们指向了同一个`Singleton`类的实例。
    std::cout << std::hex << &s1 << std::endl;
    std::cout << std::hex << &s2 << std::endl;

    return 0;
}
// static成员变量和static成员函数的生命周期和共享特性
// static成员变量和static成员函数都是属于类的,而不是属于类的任何一个对象。
// 其生命周期与程序的生命周期相同,即它们在程序中只会被创建一次,不管有多少个类的对象被创建,它们都只有一份。
// static成员变量是在程序运行时分配的内存空间,而不是在类的对象创建时分配的,因此它们可以在多个对象之间共享数据。
// static成员函数没有this指针,所以它们不能访问非静态成员变量或函数,只能访问静态成员。
// 与普通成员变量和函数的生命周期不同,普通成员变量和函数的生命周期与对象相关,即每个对象都有自己的成员变量和成员函数。
// 与类的生命周期不同,类只是一个抽象概念,没有实际的内存空间,因此类不存在生命周期的概念。
// 与对象的生命周期不同,对象是类的一个实例,有自己的内存空间,当对象被销毁时,它的成员变量和成员函数也随之销毁。

#include <iostream>

class Person {
public:
    Person(std::string name, int age) : name_(name), age_(age) {
        count_++;
    }
    static int count_;
    static void countPrint() {
        std::cout << "total number of person: " << count_ << std::endl;
    }

private:
    std::string name_;
    int age_;
};

// 定义并初始化静态成员变量
int Person::count_ = 0;

int main()
{
    Person p1("Alice", 25);
    Person p2("Bob", 30);
    Person p3("Tom", 35);
    // 由于count_是静态成员变量,所有Person对象共享该变量,因此在每次创建新对象时递增count_可以统计所有已创建的对象数量。
    // 输出总共创建的Person对象的数量为3,即使我们创建了多个对象,它们都只是count_的共享者而不是它们自己的副本。
    Person::countPrint(); // 输出3

    return 0;
}
// static成员变量的申明定义和初始化
// 在C++中,类的静态成员变量必须在类外进行定义和初始化。
// 这是因为静态成员变量不是对象的成员,而是整个类所共享的成员,因此需要在类外进行定义和初始化,以便被所有对象所共享。
// 虽然C++中的静态成员变量可以在类外定义和初始化,但是在使用该成员变量时,仍需要在类内进行声明。
// 因此,即使在类外定义和初始化了静态成员变量,仍需要在类内声明该成员变量的类型和名称。
#include <iostream>

class Person {
public:
    Person(std::string name, int age) : name_(name), age_(age) {}
    void infoPrint() const {
        std::cout << "person " << total_++ << " : name : " << name_ << ", age : " << age_ << std::endl;
    }
    // 声明静态成员变量
    static int total_;
private:
    std::string name_;
    int age_;
};

// 定义并初始化静态成员变量
int Person::total_ = 0;

int main()
{
    Person p1("Alice", 28);
    Person p2("tom", 22);
    p1.infoPrint();
    p2.infoPrint();
    return 0;
}

// c++类的static成员变量声明定义和初始化
// 在C++中,类的静态成员变量可以在类外进行初始化,也可以在类内进行初始化。
// 如果在类内进行初始化,则需要在声明时进行初始化,
// 值得注意的是,如果静态变量在类内进行初始化,则必须将其定义为const或constexpr类型
#include <iostream>

class MyClass {
public:
    // constexptr静态常量
    // 这个静态常量是在类内部定义和初始化的,因为它是一个const类型的变量。
    // 通过将静态常量定义为constexpr类型,可以将其作为编译时常量使用。
    // 如果希望在类中定义静态非常量,还是应该在类外进行初始化。
    // 这里需要注意的是,静态成员的定义和初始化应该放在同一文件中,否则编译器会出现多个定义的错误。
    // 如果不用constexpr修饰将报错:error: ISO C++ forbids in-class initialization of non-const static member ‘MyClass::MAX_NUM’
    // 如果不进行初始化将报错:error: constexpr static data member ‘MAX_NUM’ must have an initializer
    static constexpr int MAX_NUM = 100; // 已在编译期确定的常量
    // `constexpr`是C++11引入的关键字,用来指定在编译期能够计算出结果的表达式。
    // 在编写程序时使用`constexpr`可以提高代码的效率和可读性,在编译器进行编译时就已经完成了一些运算,提高运算速度,
    // 并且编译器在进行代码检查时也会对`constexpr`表达式进行检查,帮助程序员排错。
    // 在C++11之前,常量只能由`#define`和`const`定义。
    // 使用`#define`定义的常量是一种预处理器宏,不检查变量类型,也不进行类型安全检查,
    // 而使用`const`定义的常量需要在运行期进行计算,并且不方便在数组、结构体、枚举等数据类型中使用。

    // C++标准规定,constexpr函数应该满足以下要求:
    // 1. 必须有且只有一个return语句;
    // 2. 函数中不能有任何形式的循环;
    // 3. 函数中不能使用任何C++运行时库函数。

    static int factorial(int n) {
        if (n <= 1)
        {
            return 1;
        } else {
            return n * factorial(n-1);
        }
    }

    static void print() {
        static constexpr int num = 5;
        int arr[MyClass::factorial(num)];
        std::cout << "the size of the array is " << sizeof(arr) / sizeof(int) << std::endl;
    }
};

// 非静态成员变量不能再类外进行定义和初始化
// 否则报错:error: ‘int MyClass::num’ is not a static data member of ‘class MyClass’
// int MyClass::num = 0;

// 不允许在定义(而不是声明)一个静态数据成员时使用`static`关键字。
// 否则报错:error: ‘static’ may not be used when defining (as opposed to declaring) a static data member [-fpermissive]
// 所以类的static成员变量只能再类里边声明,不能再类外边声明
// static int MyClass::num = 0;

int main()
{
    std::cout << MyClass::MAX_NUM << std::endl;
    MyClass::print();
    return 0;
}


// 类的静态成员不占用类的内存空间
#include <iostream>

class MyClass0 {
public:
    MyClass0() {}
    ~MyClass0() {}

};

class MyClass1 {
public:
    MyClass1() {}
    ~MyClass1() {}

public:
    static std::string name;
    static int num;
};
std::string MyClass1::name = "Tom";
int MyClass1::num = 0;

class MyClass2 {
public:
    MyClass2() {}
    ~MyClass2() {}
    
public:
    std::string name;
    int num;
};


int main()
{
    std::cout << sizeof(MyClass0) << std::endl;
    MyClass0 obj0;
    std::cout << sizeof(obj0) << std::endl;

    std::cout << sizeof(MyClass1) << std::endl;
    MyClass1 obj1;
    std::cout << sizeof(obj1) << std::endl;

    std::cout << sizeof(MyClass2) << std::endl;
    MyClass2 obj2;
    std::cout << sizeof(obj2) << std::endl;

    return 0;
}

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值