C++ Primer 类的静态成员

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 7.6 类的静态成员
    • 声明静态成员
    • 使用类的静态成员
    • 静态成员的类内初始化
    • 静态成员能用于某些场景,而普通成员不能

7.6 类的静态成员

有的时候类需要它的一些成员与类本身直接相关,而不是与类的各个对象保持关联。例如,一个银行账户类可能需要一个数据成员来表示当前的基准利率。在此例中,我们希望利率与类关联,而非与类的每个对象关联。从实现效率的角度来看,没必要每个对象都存储利率信息。而且更加重要的是,一旦利率浮动,我们希望所有的对象都能使用新值。

声明静态成员

我们通过在成员的声明之前加上关键字static使得其与类关联在一起。和其他成员一样,静态成员可以是public的或private的。静态数据成员的类型可以是常量、引用、指针、类类型等。

举个例子,我们定义一个类,用它表示银行的账户记录:

class Account{
public:
void calculate(){amount+=amount*interestRate;}
static double rate(){return interestRate;}
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate();

类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据。因此,每个Account对象将包括两个数据成员:owner和amount。只存在一个interestRate对象而且它被所有Account对象共享。

类似的,静态成员函数也不与任何对象绑定在一起,它们不包含this指针。作为结静态成员函数不能声明成const的,而且我们也不能在static函数体内使用this。这一限制既适用于this的显式使用,也对调用非静态成员的隐式使用有效。

使用类的静态成员

我们使用作用域运算符直接访问静态成员:

double r;
r =Account::rate();//使用作用域运算符访问静态成员

虽然静态成员不属于类的某个对象,但是我们仍然可以使用类的对象、引用或者指针来访问静态成员:

Account ac1;
Account *ac2=&ac1;
//调用静态成员函数rate的等价形式
r=ac1.rate();//通过Account的对象或引用
r=ac2->rate();//通过指向Account对象的指针

成员函数不用通过作用域运算符就能直接使用静态成员:

class Account{
public:
void calculate(){amount+=amount*interestRate;}
private:
static double interestRate;
//其他成员与之前的版本一致
};定义静态成员和其他的成员函数一样,我们既可以在类的内部也可以在类的外部定义静态成员函数。当在类的外部定义静态成员时,不能重复static关键字,该关键宇只出现在类内部的声明语句:```cpp
void Account::rate(double newRate)
{interestRate=newRate;
}因为静态数据成员不属于类的任何一个对象,所以它们并不是在创建类的对象时被定义的。这意味着它们不是由类的构造函数初始化的。而且一般来说,我们不能在类的内部初始化静态成员。相反的,必须在类的外部定义和初始化每个静态成员。和其他对象一样,-个静态数据成员只能定义一次。类似于全局变量,静态数据成员定义在任何函数之外。因此一旦它被定义,就将一直存在于程序的整个生命周期中。我们定义静态数据成员的方式和在类的外部定义成员函数差不多。我们需要指定对象的类型名,然后是类名、作用域运算符以及成员自己的名字:```cpp
//定义并初始化一个静态成员
double Account::interestRate=initRate();

这条语句定义了名为interestRate的对象,该对象是类Account的静态成员,其类型是double。从类名开始,这条定义语句的剩余部分就都位于类的作用域之内了。因此,我们可以直接使用initRate函数。注意,虽然initRate是私有的,我们也能用它初始化interestRate。和其他成员的定义一样interestRate的定义也可以访问类的私有成员。

要想确保对象只定义一次,最好的办法是把静态数据成员的定义与其他非内联函数的定义放在同一个文件中。

静态成员的类内初始化

通常情况下,类的静态成员不应该在类的内部初始化。然而,我们可以为静态成员提供const整数类型的类内初始值,不过要求静态成员必须是字面值常量类型的constexpr(参见7.5.6节,第267页)。初始值必须是常量表达式,因为这些成员本身就是常量表达式,所以它们能用在所有适合于常量表达式的地方。例如,我们可以用一个初始化了的静态数据成员指定数组成员的维度:

class Account{
public:
static double rate(){return interestRate;}
static void rate(double);
private:
static constexpr int period=30;//period是常量表达式
double daily_tbl[period];
}

如果某个静态成员的应用场景仅限于编译器可以替换它的值的情况,则一个初始化的const或constexpr static不需要分别定义。相反,如果我们将它用于值不能替换的场景中,则该成员必须有一条定义语句。例如,如果period的唯一用途就是定义daily_tbl的维度,则不需要在hccount外面专门定义period。此时,如果我们忽略了这条定义,那么对程序非常微小的改动也可能造成编译错误,因为程序找不到该成员的定义语句。举个例子,当需要把Account::period传递给一个接受const int &的函数时,必须定义period。如果在类的内部提供了一个初始值,则成员的定义不能再指定一个初始值了:

//一个不带初始值的静态成员的定义
constexpr intRccount::period;//初始值在类的定义肉提供

即使一个常量静态数据成员在类内部被初始化了,通常情况下也应该在类的外部定义一下该成员。

静态成员能用于某些场景,而普通成员不能

如我们所见,静态成员独立于任何对象。因此,在某些非静态数据成员可能非法的场合,静态成员却可以正常地使用。举个例子,静态数据成员可以是不完全类型。特别的,静态数据成员的类型可以就是它所属的类类型。而非静态数据成员则受到限制,只能声明成它所属类的指针或引用:

class Bar{
public:
// ...
private:
static Bar meml;//正确:静态成员可以是不完全类型
Bar* mem2;//正确:指针成员可以是不完全类型
Bar mem3;//错误:数据成员必须是完全类型
}静态成员和普通成员的另外一个区别是我们可以使用静态成员作为默认实参:```cpp
class Screen{
public:
//bkground表示一个在类中稍后定义的静态成员
Screen &clear(char=bkground);
private::
static const char bkground;
}

非静态数据成员不能作为默认实参,因为它的值本身属于对象的一部分,这么做的结果是无法真正提供一个对象以便从中获取成员的值,最终将引发错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/70412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java——super

在Java中,super关键字用于引用父类的成员(属性、方法或构造器)。它在继承关系中非常重要,主要用于以下几种场景: 1. 调用父类的构造器 在子类的构造器中,可以使用super关键字调用父类的构造器。super()必须…

Unity 全局屏幕点击特效

思路: 1、生成一个点击特效实例,每点击屏幕,就调整特效实例的位置并控制特效的显隐状态即可。 2、需要注意要保证在编辑器开发时或手机上运行时都要显示点击效果。 方案一 (推荐) using UnityEngine; using UnityEn…

什么是业务流程分类框架

业务流程分类框架是一个用于组织和系统化地分类业务流程的结构化方法。它旨在帮助企业理解、管理、分析和改进其运营流程。 可以把它想象成一个图书馆的图书分类系统,帮助快速找到和理解不同类型的书籍。对于业务流程来说,分类框架帮助快速了解不同类型的…

基于springboot校园健康系统的设计与实现(源码+文档)

大家好我是风歌,今天要和大家聊的是一款基于springboot的园健康系统的设计与实现。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于springboot校园健康系统的设计与实现的主要使用者管理员具有最高的权限,通…

【Leetcode】平衡二叉树

平衡二叉树 题目 思路与代码实现 常规解法: int max(int a,int b){return a>b?a:b;}int maxDepth(struct TreeNode* root) {if(rootNULL)return 0;return 1max(maxDepth(root->left),maxDepth(root->right)); }bool isBalanced(struct TreeNode* root)…

【AI实践】阿里百炼文本对话Agent安卓版搭建

环境:安卓手机运行环境;WinsurfAI编程工具;阿里百炼提前创建Agent应用; 耗时:2小时; 1,新建安卓项目 完成文本输入,并将输入的文字显示出来。 2,安装SDK 参考文档 安…

一文读懂Docker之Docker Compose

目录 一、Docker Compose简介 二、Docker Compose的安装和基本使用 1、Docker Compose的安装 步骤一、下载docker-compose 步骤二、新增可执行权限 步骤三、查看是否安装成功 2、Docker Compose的基本使用 (1)、docker-compose up (2)、docker-compose ps (3)、docke…

WordPress“更新失败,响应不是有效的JSON响应”问题的修复

在使用WordPress搭建网站时,许多人在编辑或更新文章时,可能会遇到一个提示框,显示“更新失败,响应不是有效的JSON响应”。这个提示信息对于不了解技术细节的用户来说,太难懂。其实,这个问题并不复杂&#x…

信息学奥赛一本通 1973 【16NOIP普及组】买铅笔 | 洛谷 P1909 [NOIP 2016 普及组] 买铅笔

【题目链接】 ybt 1973 【16NOIP普及组】买铅笔 洛谷 P1909 [NOIP 2016 普及组] 买铅笔 【题目考点】 1. 简单数学 2. 数组 3. 向上取整 <cmath>中有函数double ceil(double x)&#xff0c;求x向上取整的值。 如果求正整数 ⌈ a b ⌉ \lceil \frac{a}{b} \rceil ⌈…

C++中的.*运算符

看运算符重载的时候&#xff0c;看到这一句 .* :: sizeof ?: . 注意以上5个运算符不能重载。 :: sizeof ?: . 这四个好理解&#xff0c;毕竟都学过&#xff0c;但.*是什么&#xff1f; 于是自己整理了一下 .* 是一种 C 中的运算符&#xff0c;称为指针到成…

【JavaEE进阶】MyBatis通过注解实现增删改查

目录 &#x1f343;前言 &#x1f340;打印日志 &#x1f334;传递参数 &#x1f38b;增(Insert) &#x1f6a9;返回主键 &#x1f384;删(Delete) &#x1f332;改(Update) &#x1f333;查(Select) &#x1f6a9;起别名 &#x1f6a9;结果映射 &#x1f6a9;开启驼…

【分布式理论14】分布式数据库存储:分表分库、主从复制与数据扩容策略

文章目录 一、分表分库1. 数据分表的必要性与方式2. 数据分库原则与优势 二、主从复制1. 读写分离架构设计2. 数据复制方式3. MySQL实现主从复制4. MySQL主从复制实践与高可用方案 三、数据扩容 随着业务的不断发展和数据量的增长&#xff0c;传统的单机关系型数据库已经逐渐不…

vxe-grid 通过配置式给单元格字段格式化树结构数据,转换树结构节点

vxe-grid 通过配置式给单元格字段格式化树结构数据&#xff0c;转换树结构节点 比如用户自定义配置好的数据源&#xff0c;通过在列中配置好数据&#xff0c;全 json 方式直接返回给前端渲染&#xff0c;不需要写任何格式化方法。 官网&#xff1a;https://vxetable.cn npm i…

延迟任务的11种实现方式(下)!!

接上文&#xff1a; Redisson的RDelayedQueue Redisson他是Redis的儿子&#xff08;Redis son&#xff09;&#xff0c;基于Redis实现了非常多的功能&#xff0c;其中最常使用的就是Redis分布式锁的实现&#xff0c;但是除了实现Redis分布式锁之外&#xff0c;它还实现了延迟…

BS5852英国家具防火安全条款主要包括哪几个方面呢?

什么是BS5852检测&#xff1f; BS5852是英国针对家用家具的强制性安全要求&#xff0c;主要测试家具在受到燃烧香烟和火柴等火源时的可燃性。这个标准通常分为四个部分进行测试&#xff0c;但实际应用中主要测试第一部分和第二部分&#xff0c;包括烟头测试和利用乙炔火焰模拟…

如何使用Spark SQL进行复杂的数据查询和分析

使用Spark SQL进行复杂的数据查询和分析是一个涉及多个步骤和技术的过程。以下是如何使用Spark SQL进行复杂数据查询和分析的详细指南&#xff1a; 一、准备阶段 环境搭建&#xff1a; 确保已经安装并配置好了Apache Spark环境。准备好数据源&#xff0c;可以是CSV文件、JSON…

iOS事件传递和响应

背景 对于身处中小公司且业务不怎么复杂的程序员来说&#xff0c;很多技术不常用&#xff0c;你可能看过很多遍也都大致了解&#xff0c;但是实际让你讲&#xff0c;不一定讲的清楚。你可能说&#xff0c;我以独当一面&#xff0c;应对自如了&#xff0c;但是技术的知识甚多&a…

FFmpeg 源码编译安装

参考&#xff1a; https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu Linux (Ubuntu) 下载 FFmpeg 源码&#xff0c;并将其解压&#xff0c;这里我将它放在 ~/ffmpeg_source 目录下&#xff1b; cd ~/ffmpeg_sources wget -O ffmpeg-snapshot.tar.bz2 https://ffmpeg.org…

【pytest】编写自动化测试用例命名规范README

API_autoTest 项目介绍 1. pytest命名规范 测试文件&#xff1a; 文件名需要以 test_ 开头或者以 _test.py 结尾。例如&#xff0c;test_login.py、user_management_test.py 这样的命名方式&#xff0c;pytest 能够自动识别并将其作为测试文件来执行其中的测试用例。 测试类…

Windows桌面系统管理5:Windows 10操作系统注册表

Windows桌面系统管理0&#xff1a;总目录-CSDN博客 Windows桌面系统管理1&#xff1a;计算机硬件组成及组装-CSDN博客 Windows桌面系统管理2&#xff1a;VMware Workstation使用和管理-CSDN博客 Windows桌面系统管理3&#xff1a;Windows 10操作系统部署与使用-CSDN博客 Wi…