本文记录我对c++ PRimer以及Introduction to programming with c++关于静态成员这一节的学习
基本概念
静态成员
从形式上来说,用static关键字声明的变量或者函数就是静态成员。它可以是public或者是private。对数据类型也没有过多的限制。 从意义上理解来说,静态成员是这样的一种成员:与类保持关联而不与类的对象保持关联。也即,类的静态成员存在于任何对象之外,而对象中也不包含任何与静态数据成员有关的数据。
简言之,静态成语是属于类的,而不属于每一个对象。
静态成员函数不能用const进行修饰
由于静态成员函数不与任何对象绑定在一起,所以他们不包含this指针。作为结果,静态成员函数不能声明成const。 这句话怎么理解?两个角度:其一,const所修饰的成员函数的语义是,该函数不修改对象的属性。也就是说,const所修饰的函数一定是和对象相关的。但是静态成员函数由于本省不与任何对象绑定,本省也无法修改对象属性,所以谈不到用const修饰。其二,const修饰的成员函数,我的理解是const本质是对this的限制,从而让this所指向的对象为常量,从而无法修改。但是,由于静态成员不含有this指针,所以没有使用const的必要。
简言之,const所修饰的成员函数一定是和对象相关的,但是静态成员是属于类的,和对象无关,所以没有const修饰的必要。
定义静态成员和初始化
因为静态成员不属于类的任何对象,所以它并不是在类的对象创建时被定义的。因此,类的构造函数不用对静态成员的初始化负责。必须在类的外部定义和初始化静态成员。对于类的成员函数则可以在类的内部或者外部定义,只是类外定义不能使用static,并且由于没有this指针,所以无法访问类的非静态变量。 简言之,对于静态数据成员,初始化一定是在类外,成员函数均可。
静态成员的类内初始化
静态数据成员一般是在外部初始化,但是有一个例外是。当这个静态数据成员本生是常量的时候。可以在类内初始化。如下代码:
#include <iostream>#include <cstring>class A{public: A(){std::memset(arr, 0, sizeof(arr));} void input(){ for(int i = 0; i < maxn; ++i){ std::cin >> arr[i]; } } void output() const { for(int i = 0; i < maxn; ++i){ std::cout << arr[i] << " "; } std::cout << std::endl; }private: static const int maxn = 8;// 静态常量,类内初始化 int arr[maxn];};int main( void ){ A a; a.output(); a.input(); a.output(); return 0;}静态成员有某些特殊的应用场景,而普通成员不行 c++ primer主要讲了两个点:
静态成员可以是不完全类型,但是普通成员则不行。当然,指针成员也可以静态成员可以作为默认实参,普通成员不行。注意 虽然,静态成员不与对象关联,我们一般是通过类去调用它们。但是,这并不影响它作为类的成员在其他成员函数当中使用。只是需要知道,静态成员函数不能访问类的成员变量,但是类的非静态成员函数,是可以访问静态成员变量的。毕竟他么也是成员变量。
下面给出一个综合实例:
代码
CircleWithStaticDataFields.h#ifndef CIRCLE_H#define CIRCLE_H#include <cstring>class Circle{public: Circle(){std::memset(this, 0, sizeof(Circle)); ++number_of_objects; } Circle( double r ) : radius_(r) { ++number_of_objects; } double get_radius() const { return radius_; } void set_radius( double r ) { radius_ = r; } double get_area() const;public: static int get_number_of_objects(); // 静态成员函数private: static double init_pi(); // 静态成员函数private: double radius_;private: static int number_of_objects; // 静态成员变量 static double PI; // 静态成员变量};#endifCircleWithStaticDataFields.cpp#include "CircleWithStaticDataFields.h"#include <cmath>int Circle::get_number_of_objects(){ // 类外定义静态成员函数,不能使用static return Circle::number_of_objects;}double Circle::init_pi(){ return std::atan(1.0)*4;}int Circle::number_of_objects = 0; // 类外对静态数据成员初始化double Circle::PI = Circle::init_pi(); // 虽然init_pi()是私有函数,也可用来初始化静态成员。毕竟还是在类的作用域内double Circle::get_area() const{ return Circle::PI * radius_ * radius_;}main.cpp#include "CircleWithStaticDataFields.h"#include <iostream>int main( void ){ std::cout << "Number of circle objects created: " << Circle::get_number_of_objects() << std::endl; Circle circle1; std::cout << "The area of the circle of the radius " << circle1.get_radius() << " is " << circle1.get_area() << std::endl; std::cout << "Number of circle objects created: " << Circle::get_number_of_objects() << std::endl; Circle circle2(1.0); std::cout << "The area of the circle of the radius " << circle2.get_radius() << " is " << circle2.get_area() << std::endl; std::cout << "Number of circle objects created: " << Circle::get_number_of_objects() << std::endl; Circle circle3(3.3); std::cout << "The area of the circle of the radius " << circle3.get_radius() << " is " << circle3.get_area() << std::endl; std::cout << "Number of circle objects created: " << Circle::get_number_of_objects() << std::endl; std::cout << "circle1.get_number_of_objects() returns " << circle1.get_number_of_objects() << std::endl; // Circle::get_number_of_objects() 这种是建议写法,读者能轻易知道这是静态成员函数 std::cout << "circle2.get_number_of_objects() returns " << circle2.get_number_of_objects() << std::endl; return 0;}