C++类中的常成员和静态成员
常变量、常对象、常引用、指向常对象或常变量的指针等在定义时都使用了const关键字,这是C++语言引入的一种数据保护机制,称为const数据保护机制。例如通过const关键字主动地将被调函数形参进行限定,限定被调函数不能修改主调函数传递过来的数据。
下面通过一个出租车类(Taxi),更好的理解常成员和常函数成员:
class Taxi //定义出租车类
{
private:
int price; //出租车里程单价
int fare; //出租车收费总额
public:
void SetPrice(int p) { price = p; } //设置里程单价
int GetPrice() { return price; } //获取里程单价
void SetFare(int f) { fare = f; } //设置收费总额
int GetFare() { return fare; } //获取收费总额
void AddFare(int f) { fare += f; } //累计收费总额
void Order() //执行一个打车订单
{
int x = GetARand(5,100); //通过随机数获得一次订单里程
AddFare(x*price); //计算一次订单金额,加到收费总额里
}
Taxi(int p =0,int f=0) //带默认形参值的构造函数
{
price = p;
fare = f;
}
}
一、常成员
在定义类时,使用const关键字进行限定的成员称为常成员。数据成员和函数成员均可定义为常成员。
1、常数据成员
如果一个数据成员所保存的数值在初始化以后不会改变,那么可以将这个数据成员定义成常数据成员。换句话说,常数据成员只能在对象初始化时赋值,初始化后不得再修改。常数据成员和常变量的不同在于常数据成员在类中声明时不得初始化,类中任何数据成员的初始化必须经过构造函数完成。
常数据成员的语法细则:
1)常数据成员定义:const 数据类型 常数据成员名;
2)初始化列表:为构造函数添加初始化列表是对常数据成员进行初始化的唯一途径。语法形式如下:
构造函数名(形参列表):常数据成员名1(形参1),常数据成员名2(形参2),...
{
//函数体中对其他数据成员初始化
}
以出租车里程单价price为常数据成员为例:
const int price;
Taxi(int p=0;int f=0):price(p)
{
fare=f;
}
在构造函数头后面添加初始化列表“:price(p)”是唯一能够设置常数据成员price初始值的地方,其他任何地方都不得对price再次赋值。
3) 定义对象时初始化。定义含常数据成员类的对象时需要初始化,重点给出常数据成员的初始值。
程序员在设计类的时候,如果认为某个数据成员所保存的数值在初始化以后不能在被修改,可以主动将其定义为常数据成员。
2、常函数成员
如果某个函数成员只需要读取类中的数据成员(注意:不是常数据成员)的数值,而不会修改它们,那么可以将其定义为常函数成员。
常函数成员定义语法形式:
1)内联函数。在类声明部分直接定义的函数被当作内联函数处理,在函数头后面加const关键字就可以将其定义为常函数成员。
示例:int GetPrice() const { return price; }
2) 非内联函数。此时需要在声明和定义语句的函数头后面分别加上const关键字。
示例:
类声明中:int GetPrice() const;
类实现中:int Taxi::GetPrice() const { return price; }
常函数成员语法细则:
1)声明、定义常函数成员须在函数头后面加关键字const进行限定。
2)常函数成员只能读取类中数据成员的数值,不能修改它们。
3)常函数成员只能调用其他常函数成员,以防止常函数通过其他函数间接修改数据成员。
4)通过常对像只能调用其常函数成员,以防止函数间接修改常对象的数据成员。
5)除了形参的个数和类型之外,还可以用关键字const区分类中的重载函数。
3、关于const重载类成员函数的问题
1)如何调用重载后的两个函数?
const修饰的对象调用的是使用const修饰的方法,非const对象调用的是非const的方法。
2)重载是如何实现的?
经查资料,这些函数的参数中其实还有一个隐式的this指针,在调用的时候传入。因为在非const修饰的函数中,this指针也是非const的。在const修饰的函数中,this指针是const修饰的。所以非const对象调用函数时,this指针是非const的,调用非const函数。const对象调用函数时,this指针是const的,调用的是const的函数。
注意:如果一个函数用const修饰了,但是这个函数没有实现重载,那么非const对象和const对象都能调用这个函数。
特别注意的是,const修饰的对象只能调用const修饰的函数。
二、静态成员
面向对象的程序设计希望用类管理所有程序代码,使得程序中没有游离在类外的全局变量和外部函数。对于一些共用的全局变量或外部函数,可以将它们规划到某个具有关联关系的类中,与类中的其他成员一起进行统一管理。定义类时,使用关键字static进行限定的成员称为静态成员。数据成员和函数成员都可以定义为静态成员。与静态全局变量、静态局部变量和静态函数一样静态成员的定义与作用域密不可分。
1、静态数据成员
静态数据成员属于一个类整体,不属于某个对象。作用上相当于全局变量,使用起来类似静态局部变量(静态局部变量作用域是某个函数,生存期是程序全局)。
静态数据成员的语法形式:
类声明:static 数据类型 静态数据成员名;//注意这里只是对静态数据成员的声明,未继进行定义。
初始化:数据类型 类名::静态数据成员名 = 某值;//注意,静态数据成员的定义只能在类实现中,即类实现部分定义静态数据成员。定义时可以初始化。(那么在类定义时初始化的静态数据成员如何分配的内存空间哪?又如何将该初始化值传递给调用该类的其他程序哪?)
示例:
类声明:static int totalFare;
初始化:int Taxi::totalFare = 0;
静态数据成员可以是私有的(只能被该类的成员函数访问),也可以是公有的(可以被类外其他函数访问修改)。
静态数据成员的语法细则:
1)关键字static。在类中声明静态数据成员需使用关键字static进行限定,声明时不能初始化。
2)定义和初始化。必须在类声明的大括号后面对静态数据成员进行定义,定义时不能加static关键字。定义时可以初始化。
3)在类的函数成员中访问。类中的函数成员直接使用成员名访问静态数据成员,访问时不受权限约束。这一点和普通数据成员一样。
4)在类外其他函数中访问。在类外其他函数中访问静态数据成员需以“类名::静态数据成员名”的形式访问,或通过任何一个该类对象以"对象名.静态数据成员"的形式访问,或通过任何一个该类对象指针以“对象指针名->静态数据成员名”的形式访问。
私有静态数据成员具有类作用域,只能在类内访问。公有静态数据成员具有文件作用域,可以被本文件的任何函数访问,并且可通过类声明将其作用域扩展到任何程序文件。
5)内存分配。和全局变量一样,静态数据成员也是静态分配,被分配在静态存储区。静态数据成员在程序加载后立即分配内存,知道程序执行结束退出时才被释放。这一点与类中的其他不同数据成员时完全不同的。类定义多个对象,每个对象的普通数据成员都各自分配内存单元,但所有对象额静态数据成员会共用一个内存单元。
2、静态函数成员
可以将外部函数划归到某个具体关联关系的类中,作为类的静态函数成员进行管理。
静态函数成员的语法形式:
类声明:static 返回值类型 静态函数成员名(形参列表);
类实现:返回值类型::静态函数成员名(形参列表) { 函数体 }
示例:
类声明:static void AddTotal(int f);
类实现: void Taxi::AddToal(int f) {totalFare += f; }
静态函数成员的语法细则:
1)声明时使用关键字static进行限定,定义时不能在使用关键字static。
2)类中的其他函数成员可以调用静态函数成员。调用时直接使用函数名,不受访问控制权限约束。
3)在类外调用静态函数成员需以“类名::静态函数成员名”的形式访问,或通过任何一个该类对象以"对象名.静态函数成员"的形式访问,或通过任何一个该类对象指针以“对象指针名->静态函数成员名”的形式访问。
类外调用静态函数成员受访权限约束,私有静态函数成员具有类作用域,只能在类内调用。公有静态函数成员具有文件作用域,可以被本文件的任何函数调用,并且可通过类声明将其作用域扩展到任何程序文件。
4)静态函数成员只能访问类中的静态数据成员,也就是不能访问类中的非静态数据成员。因为静态函数成员可以在没有对象定义的情况下被调用,此时静态数据成员还没有分配内存空间,不能访问。同样,静态函数成员只能调用类中的静态函数成员,不能调用非静态函数成员。
5)静态函数成员不能是内联函数,因为编译器在编译时会调整内联函数,可能会用到静态数据成员中的数据,但此时静态数据成员还未初始化,所访问的数据是错误的。
3、静态成员的应用
1)以类的形式管理全局变量和外部函数
本质上静态数据成员就是一个全局变量,静态函数成员就是一个外部函数。将它们改为静态成员好处便于分类组织和管理程序代码。
2)将具有相同属性值的成员定义成静态数据成员
利用静态数据成员公用内存单元的特点,可以将具有想用属性的成员定义成静态数据成员,这样就有效减少内存占用。
例如:每个出租车对象都有price里程单价这一数据成员,现实中相同的出租车里程单间一样,这样为每个出租车都设置一个price就显得有些内存浪费,设置一个静态数据成员price表示全部出租车类的里程单价,就会节省很多内存资源。
C++类中的常成员和静态成员的更多相关文章
- cc31a_demo--CppPrimer_静态成员与继承-在派生类中访问基类中的static成员的方法
//*基类中的static成员,在整个继承层次中只有一个实例 //*在派生类中访问基类中的static成员的方法 //1.基类名::成员名 //2.子类名::成员名 //3.对象.成员名 //4.指针 ...
- 在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static
在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static ! 在C语言中,我们使用pthread_create ...
- C++ 类中的static成员的初始化和特点
C++ 类中的static成员的初始化和特点 #include <iostream> using namespace std; class Test { public: Test() : ...
- 类中的internal成员可能是一种坏味道
前言 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原先只是有一丝细微的感觉,而现在将它和一些其他的方面进行了联 ...
- C++ 类中特殊的成员变量(常变量、引用、静态)的初始化方法
有些成员变量的数据类型比较特别,它们的初始化方式也和普通数据类型的成员变量有所不同.这些特殊的类型的成员变量包括: a.引用 b.常量 c.静态 d.静态常量(整型) e.静态常量(非整型) 常量和引 ...
- C/C++中的常成员函数
代码: #include <iostream> using namespace std; class A{ public: void func1(){ cout<<" ...
- c++:类中的static成员
首先静态成员可以是public的,也可以是private的,只需在一般的变量.函数声明语句前加上static关键字即可声明一个static变量. 类中的静态成员存在与任何对象之外,所有该类对象的共享一 ...
- [转]C++ 类中的static成员的初始化和特点
在C++的类中有些成员变量初始化和一般数据类型的成员变量有所不同.以下测试编译环境为: ➜ g++ -v Using built-in specs. COLLECT_GCC=g++ Target: x ...
- 【c++】类中的const成员
const成员变量 举个例子 #include <iostream> using namespace std; class A { public: A(int size) : SIZE(s ...
随机推荐
- vue 时间过滤器
过滤器:定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理).语法:1.注册过滤器: Vue.filter(name ,callback)或new Vue{filters:{}}2. ...
- Kafka核心组件详解
1.概述 对于Kafka的学习,在研究其系统模块时,有些核心组件是指的我们去了解.今天给大家来剖析一下Kafka的一些核心组件,让大家能够更好的理解Kafka的运作流程. 2.内容 Kafka系统设计 ...
- 移动端input的disabled属性对字体颜色影响
对于表单输入,input是很好的选择,这次记录主要是正对input的value值字体在Android和iOS(11)设备下显示不同问题: 如下图:1.2的区别主要是分别设置disabled.reado ...
- KeyDB重量发布6.3.0开源版
摘要:5月12日 KeyDB 社区隆重发布了 6.3.0开源版本,将与华为加拿大研究院DCS团队2021-2022年合作的成果,深度优化的企业版的能力贡献给了开源社区. KeyDB是目前Redis 分 ...
- spring boot 默认日志替换为 log4j
移除默认日志 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- 流,用声明性的方式处理数据集 - 读《Java 8实战》
引入流 Stream API的代码 声明性 更简洁,更易读 可复合 更灵活 可并行 性能更好 流是什么? 它允许以声明方式处理数据集合 遍历数据集的高级迭代器 透明地并行处理 简短定义:从支持数据处理 ...
- 206. Reverse Linked List - LeetCode
Question 206. Reverse Linked List Solution 题目大意:对一个链表进行反转 思路: Java实现: public ListNode reverseList(Li ...
- linux下三种服务开机自启的方式
方式一.二.三适用于ubuntu,centos推荐使用方式二.方式三 方式一 在ubuntu系统中,如果你使用的apt方式安装的软件,可以使用如下方式直接添加服务的开机自启, 如果你是手动解压缩官网下 ...
- 好客租房11-为什么脚手架使用jsx语法
为什么脚手架中可以使用jsx语法 1jsx不是标准的ECMAScript ,他是ECMAScript的语法扩展 2需要使用babel编译处理后 才能在浏览器环境中使用 3create-react-ap ...
- 给IDEA道个歉,这不是它的BUG,而是反编译插件的BUG。
你好呀,我是歪歪. 上周我不是发了<我怀疑这是IDEA的BUG,但是我翻遍全网没找到证据!>这篇文章吗. 主要描述了在 IDEA 里面反编译后的 class 文件中有这样的代码片段: 很明 ...