[c/c++] static 关键字

从修饰的对象来看,static 可以修饰局部变量,也可以修饰全局变量,可以修饰函数;可以修饰类中的成员变量以及成员函数。

从生命周期的角度来看,static 修饰的对象的生命周期,与进程的生命周期是一致的。

从作用域的角度来看,局部静态变量的作用域是函数内,全局静态变量的作用域是模块内;类中的静态成员变量和成员函数的作用域,能访问到类的地方都可以访问到静态成员,类中的静态成员同时也受 public,private,protected 权限修饰符的影响。在 c 语言中,静态函数的作用域只在模块内,这样也是一种权限管理,不让其它模块来访问,增强了安全性;同时,多个模块中的两个静态函数的函数名也可以是相同的,给开发带来了方便。

1 static 修饰变量

1.1 局部静态变量

如下代码,在函数 Test() 中声明定义了一个静态变量 static int a = 10,局部静态变量有以下 3 点需要注意:

(1)作用域是函数内

(2)在 static int a = 10,只在启动的时候初始化一次,不像普通的变量,Test() 每次调用的时候都会将 a 赋值成 10。代码中的 a++ 分别是 11,12,13 ...

(3)在声明的时候需要初始化,并且初始化的值必须得是确定的

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void Test(int data) {
  // static int a = data; // 这样编译不过,编译的时候就需要确定  error: initializer element is not constant
  static int a = 10;
  a++;
  printf("a = %d, data = %d\n", a, data);
}

int main() {
  for (int i = 0; i < 10; i++) {
    Test(i);
  }
  return 0;
}

运行结果如下:

1.2 全局静态变量

在 c 语言中,全局静态变量的作用域就是在源文件内,一个 c 文件中的函数无法访问到另一个 c 文件中声明的静态全局变量。

文件 s0.c 中定义一个静态变量:

static int static_ele = 100;

文件 s0.h 中使用 extern 声明了这个变量:

extern int static_ele;

文件 s2.c 中包含了头文件 s0.h,并且尝试访问变量 static_ele。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "s0.h"

static int a = 100;

int main() {
  printf("a = %d, static_ele = %d\n", a, static_ele);
  return 0;
}

编译会报错,在 s2.c 中无法访问到 static_ele。

2 static 修饰函数

static 修饰的函数也是只能在 c 文件内部访问,在其它 c 文件不能访问。
 

文件 s3.c 中定义一个静态函数 static void SayHello():

#include <stdlib.h>
#include <stdio.h>

static void SayHello() {
  printf("hello in static function\n");
}

void SayHello1() {
  printf("hello1\n");
}

文件 s3.h 中是函数的声明:

void SayHello();
void SayHello1();

文件 s4.c 中包含了头文件 s3.h,并尝试调用函数 SayHello():

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "s3.h"

int main() {
  SayHello();
  SayHello1();
  return 0;
}

 编译报错,找不到函数定义:

在 c 语言中,经常使用函数指针,如果静态函数被一个函数指针维护,那么其它模块可以通过函数指针来调用静态函数的。static 关键字是编译阶段检查的语法,在运行时无法检查。

对 s3.c,s3.h,s4.c 进行修改,使用一个函数指针来保存静态函数 SayHello() 的地址,这样的话只要能拿到这个函数指针,就能调用对应的函数。

s3.c:

#include <stdlib.h>
#include <stdio.h>
#include "s3.h"

static void SayHello() {
  printf("hello in static function\n");
}

void SayHello1() {
  printf("hello1\n");
}

struct FunctionPointer fp = {
  .say_hello = SayHello
};

s3.h

void SayHello1();

struct FunctionPointer {
  void (*say_hello)();
};

extern struct FunctionPointer fp;

s4.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "s3.h"

int main() {
  fp.say_hello();
  SayHello1();
  return 0;
}

编译运行结果如下:

3 static 修饰类成员

3.1 静态成员变量

3.1.1 使用到的静态成员,需要在类外初始化

静态成员变量属于类,不属于对象。所有的对象共享一份静态成员变量,而不是每个对象都拥有一个。

静态成员变量有以下几点需要注意,在代码中通过注释做了说明。

(1)需要在类外初始化

(2)在类外初始化的时候不能使用 static 修饰

(3)静态成员变量可以被 public,private,protected 修饰,访问权限与非静态成员变量是一致的

#include <iostream>
#include <string>

class Test {
public:
  Test() {
    std::cout << "Test()" << std::endl;
    public_a = 1;
    private_a = 2;
    protected_a = 3;
  }

  ~Test() {
    std::cout << "~Test()" << std::endl;
  }

  void Do() {
    std::cout << "public a = " << public_a << std::endl;
    std::cout << "private a = " << private_a << std::endl;
    std::cout << "protected a = " << protected_a << std::endl;
  }

  static int public_a;

private:
  static int private_a;

protected:
  static int protected_a;
};

// 类的静态成员变量在类外初始化的时候
// 不能再加 static 关键字
// 如果还带 static 关键字,会报错
// error: ‘static’ may not be used when defining (as opposed to declaring) a static data member
// static int Test::public_a = 10;
// static int Test::private_a = 20;
// static int Test::protected_a = 30;

// 类的静态成员变量需要在类外初始化
int Test::public_a = 10;
int Test::private_a = 20;
int Test::protected_a = 30;

int main() {
  Test t;
  t.public_a = 100;
  t.Do();
  // t.private_a = 200;
  // t.protected_a = 300;
  Test::public_a = 1000;
  // Test::private_a = 200;
  // Test::protected_a = 300;
  t.Do();
  return 0;
}

 运行结果如下图所示:

3.1.2 没有使用的静态成员,不初始化也不会报错

如下代码,在类 Test2 中声明了两个静态成员变量,并且代码中也没有访问这两个成员,那么编译是不会报错的。

#include <iostream>
#include <string>

class Test1 {
public:
  void Do1() {
    std::cout << "Do1()\n";
  }

  Test1(int data) {
    std::cout << "Test1()\n";
    a = data;
  }

  int a;
};

class Test2 {
  static Test1 t1;
  static int a;
public:
  void Do2() {
    std::cout << "Do2" << std::endl;
  }
};

int main() {
 Test2 t2;
 t2.Do2();
 return 0;
}

3.2 静态成员函数

3.2.1 静态成员函数中没有 this 指针

在 c++ 中,static 修饰类成员函数的时候,那么这个函数可以直接通过 类名::函数名 的方式来访问。c++ 静态成员函数中没有 this 指针。

如下代码中 Test 类中有一个静态函数,一个非静态函数。对于非静态函数,在实际调用的时候会将 this 指针传递给函数,Do1() 的一个入参是 const Test *this;而对于静态函数来说,不会传 this 指针。

#include <iostream>
#include <string>

class Test {
public:
  void Do1() {
    std::cout << "Do1()\n";
  }

  static void Do2() {
    std::cout << "Do2()\n";
  }
};

int main() {
 Test t;
 t.Do1();
 t.Do2();
 return 0;
}

静态成员函数有以下几点需要注意:

(1)可以在类内部声明和实现

(2)也可以在类内部声明,在类外部实现,在类外部实现时,不能 static 修饰

(3)静态成员函数中不能调用非静态成员函数,也不能使用非静态成员变量。因为静态函数中没有 this 指针,而访问非静态函数以及非静态成员变量的时候,需要 this 指针

#include <iostream>
#include <string>

class Test {
public:
  Test() {
    std::cout << "Test()" << std::endl;
    public_a = 1;
    private_a = 2;
    protected_a = 3;
  }

  ~Test() {
    std::cout << "~Test()" << std::endl;
  }

  void Do() {
    std::cout << "public a = " << public_a << std::endl;
    std::cout << "private a = " << private_a << std::endl;
    std::cout << "protected a = " << protected_a << std::endl;
  }

  static void StaticDo() {
    // Do(); // error: cannot call member function ‘void Test::Do()’ without object
    // public_b = 10; // error: invalid use of member ‘Test::public_b’ in static member function
    public_a = 10;
    std::cout << "StaticDo" << std::endl;
  }

  static void StaticDo1();

  static int public_a;
  int public_b;

private:
  static int private_a;

  static void PrivateStaticDo() {
    std::cout << "Private StaticDo" << std::endl;
  }

protected:
  static int protected_a;

  static void ProtectedStaticDo() {
    std::cout << "Protected StaticDo" << std::endl;
  }
};

int Test::public_a = 10;
int Test::private_a = 20;
int Test::protected_a = 30;

// error: cannot declare member function ‘static void Test::StaticDo1()’ to have static linkage
/* static */ void Test::StaticDo1() {
  std::cout << "StaticDo1()" << std::endl;
}

int main() {
  Test t;
  t.StaticDo();
  t.StaticDo1();
  // t.PrivateStaticDo();
  // t.ProtectedStaticDo();
  // Test::PrivateStaticDo();
  // Test::ProtectedStaticDo();
  return 0;
}

代码运行结果如下:

3.2.2 静态成员函数不能是虚函数

当 virtual 和 static 同时修饰一个函数的时候,报编译错误。

static 函数是属于类的,可以直接通过 类名:: 函数名 的方式来访问。

而虚函数的访问,必须要通过对象来访问,因为只有通过对象才能找到虚函数表,虚函数表的地址保存在对象的一开始的位置。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值