对象的使用

本文详细介绍了C++中的static用法,包括static成员、static成员函数、对象的生命周期和作用域。static成员作为类关联的对象,不依赖于具体实例,可用于统计对象数量等。此外,还探讨了static与单例模式的关系以及const成员函数和const对象的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

static成员


  • 对特定类型的全体对象而言,有时候需要访问一个全局的变量。比如说统计某种类型对象已创建的数量;
  • 如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量,这是我们可以用类的静态成员来解决这个问题;
  • 非static数据成员存在于类类型的每个对象中,static数据成员独立该类的任意对象存在,它是与类关联的对象,不与类对象关联。

static成员声明、定义与初始化


  • static成员需要在类定义体外进行初始化与定义
/*---------------------示例代码------------------------*/
//CountedObject.h 
#ifndef _COUNTED_OBJECT_H_
#define _COUNTED_OBJECT_H_

class CountedObject
{
public:
    CountedObject();
    ~CountedObject();
    static int count_; //静态成员的引用性声明
};
#endif  //_COUNTED_OBJECT_H_
//CountedObject.cpp 
#include "CountedObject.h"

int CountedObject::count_ = 0;  //静态成员的定义性声明,此时不需要指定static
                                //静态成员的初始化也必须放在文件作用域
CountedObject::CountedObject()
{
    ++count_;
}
CountedObject::~CountedObject()
{
    --count_;
}
//01.cpp
#include "CountedObject.h"
#include<iostream>
using namespace std;

int main(void)
{
    CountedObject ob1;
    cout << CountedObject::count_ <<endl;
    CountedObject* ob2 = new CountedObject;
    cout << CountedObject::count_ <<endl;
    delete ob2;
    cout << CountedObject::count_ <<endl;
    return 0;
}

特殊的整型static const成员:整型static const成员可以在类定义体中初始化

#include<iostream>
using namespace std;

class Test
{
public:
    Test()
    {}
    ~Test()
    {}
    static const int x_ = 100;    //静态常量整型成员的初始化可以直接在类体内进行
};
int main(void)
{
    cout<<Test::x_<<endl;
}

static成员优点

  • static成员的名字是在类的作用域中,因此可以避免与其它类成员或全局对象名字冲突
  • 可以实施封装,static成员可以是私有的,而全局对象不可以
  • 阅读程序容易看出static成员与某个类相关联,这种可见性可以清晰地反映程序员的意图

当静态成员声明为私有时,需要提供接口函数

#ifndef _COUNTED_OBJECT_H_
#define _COUNTED_OBJECT_H_

class CountedObject
{
public:
    CountedObject();
    ~CountedObject();
public:
    static int GetCounnt();//为私有的静态成员提供接口函数
private:
    static int count_; //静态成员的引用性声明
};
#endif  //_COUNTED_OBJECT_H_

int CountedObject::GetCounnt()  //接口函数
{
    return count_;
}

static成员函数


  • static成员函数没有this指针
  • static成员函数不可以访问非静态成员和非静态成员函数
  • 非静态成员函数可以访问静态成员
class Test
{
public:
    Test(int y) : y_(y)
    {
    }
    ~Test()
    {
    }
    void TestFun()
    {
        cout<<"x="<<x_<<endl;   //OK,非静态成员函数可以访问静态成员
        TestStaticFun();
    }
    static void TestStaticFun()
    {
        cout<<"TestStaticFun ..."<<endl;
        //TestFun();        Error,静态成员函数不能调用非静态成员函数
        //cout<<"y="<<y_<<endl;     Error,静态成员函数不能访问非静态成员
    }
    static int x_;      // 静态成员的引用性说明
    int y_;
};

类/对象大小计算

  • 类大小计算遵循前面学过的结构体对齐原则
  • 类的大小与数据成员有关与成员函数无关
  • 类的大小与静态数据成员无关
  • 虚函数对类的大小的影响
  • 虚继承对类的大小的影响

四种对象的作用域与生存期

栈对象

隐含调用构造函数(程序中没有显示调用)

堆对象

隐含调用构造函数(程序中没有显示调用)

全局对象、静态全局对象

  • 全局对象的构造先于main函数
  • 已初始化的全局变量或静态全局对象存储于.data段中
  • 未初始化的全局变量或静态全局对象存储于.bss段中

静态局部对象

  • 已初始化的静态局部变量存储于.data段中
  • 未初始化的静态局部变量存储于.bss段中

内存分区

static用法总结

C语言中的用法

  • 用于函数内部修饰变量,即函数内的静态变量。这种变量的生存期长于该函数,使得函数具有一定的“状态”。使用静态变量的函数一般是不可重入的,也不是线程安全的,比如strtok(3);
  • 用在文件级别(函数体之外),修饰变量或函数,表示该变量或函数只在本文件可见,其他文件看不到也访问不到该变量或函数。专业的说法叫“具有internal linkage”(简言之:不暴露给别的translation unit)。

C++中添加的用法

  • 用于修饰类的数据成员,即所谓“静态成员”。这种数据成员的生存期大于class的对象(实例/instance)。静态数据成员是每个class有一份,普通数据成员是每个instance 有一份;
  • 用于修饰class的成员函数,即所谓“静态成员函数”。这种成员函数只能访问静态成员和其他静态程员函数,不能访问非静态成员和非静态成员函数。

static与单例模式

  • 保证一个类只有一个实例,并提供一个全局访问点
  • 禁止拷贝
#include<iostream>
using namespace std;
class Singleton
{
public:
    static Singleton* GetInstance()  //2. 提供一个全局访问点
    {
        if(instance_ == NULL)
        {
            instance_ = new Singleton; //构造
        }
        return instance_;
    }
    ~Singleton()
    {
        cout << "~Singleton ..."<<endl;
    }
private:
    Singleton()                     //1. 将构造函数声明为私有的
    {
        cout<< "Singleton ..."<<endl;
    }
    Singleton(const Singleton& other);            //
    Singleton& operator=(const Singleton& other); //3. 将拷贝构造函数与等号运算符都声明为私有的,斌企鹅不提供其实现方式,那么就可以禁止拷贝了
    static Singleton* instance_;
};
Singleton* Singleton::instance_;
int main(void)
{
    Singleton* s1 = Singleton::GetInstance();//
    Singleton* s2 = Singleton::GetInstance();//  二者调用的是同一个实例
    return 0;
}

需要注意的是以上程序运行时系统是不会调用析构函数的,所以需要进一步改造
程序结果

  • 方案一:
#include<iostream>
using namespace std;
class Singleton
{
public:
    static Singleton* GetInstance()
    {
        if(instance_ == NULL)
        {
            instance_ = new Singleton; //
        }
        return instance_;
    }
    ~Singleton()
    {
        cout << "~Singleton ..."<<endl;
    }
    class Garbo   //1. 提供一个嵌套类,在该类中进行释放
    {
    public:
        ~Garbo()
        {
            if(Singleton::instance_ != NULL)
            {
                delete Singleton::instance_;
            }
        }
    };
private:
    Singleton()
    {
        cout<< "Singleton ..."<<endl;
    }
    Singleton(const Singleton& other);
    Singleton& operator=(const Singleton& other);
    static Singleton* instance_;
    static Garbo garbo_;    //2. 当该对象生存周期结束时自动调用析构函数,从而达到
};
Singleton::Garbo Singleton::garbo_; // 3. 此处必须声明
Singleton* Singleton::instance_;
int main(void)
{
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    return 0;
}
  • 方案二(推荐):
#include<iostream>
using namespace std;
class Singleton
{
public:
    static Singleton& GetInstance()  //全局访问点
    {
        static Singleton instance;   //局部静态成员
        return instance;
    }
    ~Singleton()
    {
        cout << "~Singleton ..."<<endl;
    }
private:
    Singleton()
    {
        cout<< "Singleton ..."<<endl;
    }
    Singleton(const Singleton& other);
    Singleton& operator=(const Singleton& other);
};
int main(void)
{
    Singleton& s1 = Singleton::GetInstance();
    Singleton& s2 = Singleton::GetInstance();
    return 0;
}

const成员函数

  • const成员函数不会修改对象的状态
  • const成员函数只能访问数据成员的值,而不能修改它
class Test
{
public:
    Test(int x):x_(x)
    {}
    int GetX() const
    {
        return x_;
    }
private:
    int x_;
};

const对象

  • 如果把一个对象指定为const,就是告诉编译器不要修改它;
  • const对象的定义:
    const 类名 对象名(参数表)
  • const对象不能调用非const成员函数
int main(void)
{
    const Test t(10);  //const对象,对象的状态不能被更改,不能调用非const成员函数
    cout << t.GetX() << endl; //GetX()为const成员函数,因此t可以调用
    return 0;
}

mutable

  • 用mutable修饰的数据成员即使在const对象或在const成员函数中都可以被修改
class Test
{
public:
    Test(int x):x_(x),outputtimes_(0)
    {}
    int GetX() const
    {
        return x_;
    }
    void OutPutX() const
    {
        cout << "x = "<<x_<<endl;
        outputtimes_++;       //outputtimes_已被声明为mutable,此处可以修改它的值
    }
    int GetOutPutTimes() const
    {
        return outputtimes_;
    }
private:
    int x_;
    mutable int outputtimes_;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值