类的静态成员学习

本文记录我对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; // 静态成员变量
};

#endif
  • CircleWithStaticDataFields.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;
}

下面在写一段测试代码,用来说明静态成员如何初始化,静态成员函数的两种常用情形:

  • 操作静态成员
  • 当对象不存在的时候使用类方法。

静态成员函数私有和非私有的区别和之前没有区别,如果这个接口你不希望用户访问,那就是私有的。

#include <iostream>

class Foo{
public:
    static int get_num(); // 操作静态变量
private:
    static int init_num(); // 在没有对象的时候,使用方法。
    static int num;
};

int Foo::init_num(){
    return 100;
}
int Foo::get_num(){
    return num;
}

int Foo::num = Foo::init_num(); // legal 合法,左值在类作用于,右值也在,都在类作用域。

int main( void ){
    //Foo::init_num(); // illegal 非法,因为是private成员
    std::cout << Foo::get_num() << std::endl;

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值