35

c++中的静态成员变量

 5 years ago
source link: https://www.tlanyan.me/static-member-in-cpp/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

转载请注明文章出处: https://tlanyan.me/static-member-in-cpp

C++的 static 关键字可修饰 成员变量/方法,表示变量/方法不从属于特定对象,而是属于类的。仔细琢磨静态成员变量,会发现其与C++的方式既相容也矛盾,具有特殊性。

先说相容的一面。·C/C++·有声明和定义的说法:声明给出签名,定义给出具体实现。对 类型 而言, 声明 不一定能知道其对象占用空间大小,但根据 定义 肯定能确定内存占用。说静态成员与C++方式是相容的,因为其 初始化方式 与方法的定义一致。下面是一个例子:

// Foo.hpp
namespace tlanyan {
  // 类声明和定义
  class Foo {
  private:
    // 声明并定义静态成员
    static int value;
  public:
    // 方法声明
    void increaseValue();
    int getValue() const;
  };
}

// Foo.cpp
namespace tlanyan {
  // 静态成员变量初始化
  int Foo::value = 0;
  // 类方法定义
  void Foo::increaseValue() {
    ++ value;
  }
  int Foo::getValue() {
    return value;
  }
}

相对于相容点,静态成员变量更多展现出怪异的一面,以下是个人总结:

  1. 静态成员不能在类中初始化;非静态成员可直接初始化,静态成员在声明的同时也给出了定义,但就是不能在类中定义处直接初始化。辅以 const 的静态成员可以直接初始化,但那是 const 的能力而非 static 所有;
  2. 对静态成员初始化,需要在类之外重新定义一次;如上例所示,静态成员初始化的形式如同其定义,需加上类型信息 int ;好处时保证只能初始化一次,缺点则是繁琐的重新定义;
  3. 初始化时不受访问修饰符限制; private 类型的静态成员可直接访问并赋值;
  4. 静态成员初始化时可调用函数,并且可以直接调用 所属类的私有函数

其中第4点比较重要。在不支持C++11的编译器上,要完成静态 map 成员,就不得不借助函数返回:

#include <map>
// 类定义
class Foo {
private:
  std::map<const char*, int> maps;
  ...
}
// 静态成员初始化
std::map<const char*, int> Foo::maps = Foo::initMap();
// 或者使用全局函数
std::map<const char*, int> Foo::maps = initMap();

C++11 引入了统一初始化和 lambda 表达式,初始化的写法更为简单:

// 统一初始化
std::map<const char*, int> Foo::maps {
  {"a", 31},
  {"b", 32}
};
// lambda表达式方式
std::map<const char*, int> Foo::maps = [] {
  map<const char*, int> _map;
  _map.insert(map<const char*, int>::value_type("a", 31));
 _map.insert(map<const char*, int>::value_type("a", 32));
  return _map;
}();

静态成员的这些异常行为很容易联想到 全局变量 ,两者有许多相通的地方:在程序启动前完成初始化,在程序终止后销毁;存放的地方都是静态存储区而非堆栈;通过名字空间操作符获取值;在非函数块内通过函数调用或者lambda表达式完成初始化…

虽然各种面向对象编程语言都有静态变量,并且使用比例不低。但从 面向对象 的角度,静态成员是另一种形式的全局变量,其破坏了隔离和封装,增加了类之间的耦合,让测试变得更困难。实际编程中,应当慎用全局变量,并收紧其访问权限。

所以本质上静态成员也是全局变量,只是归属到特定类的名下。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK