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 ...
随机推荐
- Java SPI 和 API,傻傻分不清?
最近新写了一个中间件「运行时动态日志等级开关」,其中使用Java SPI机制实现了自定义配置中心,保证良好的扩展性. 项目地址,走过路过可以点个star :)https://github.com/sa ...
- Node.js 中的进程和线程
线程和进程是计算机操作系统的基础概念,在程序员中属于高频词汇,那如何理解呢?Node.js 中的进程和线程又是怎样的呢? 一.进程和线程 1.1.专业性文字定义 进程(Process),进程是计算机中 ...
- C# Thread.Sleep 不精准的问题以及解决方案
1.问题 最近在写一个熔断的 SDK,其中一种策略是根据慢请求来进行熔断. 我们在测试的时候,在对应 API 里面采用了 Thread.Sleep(ms) 来模拟慢请求. 设置的慢请求阈值是 RT 1 ...
- 好客租房2-React概述
1.1什么是react React是一个用于构建用户界面的javascript库 用户界面:HTML页面 React主要用来HTML 或者沟通构建web应用 如果从MVC的角度来看 react仅仅是从 ...
- 146_ACCESS之HR招聘信息管理_64位
焦棚子的文章目录 点击下载附件 一.背景: 最近把之前做的一个HR招聘信息管理工具翻新了下,有需要的朋友可以自取,主要想解决的问题是多人在跟进人员招聘的时候信息的不对称,这样下来的就可以及时的看到整个 ...
- README.exe 是的,你看错是EXE
SmartIDE让你的README变成可执行文档,再也不用编写无用的文档,再也不必操心环境问题. 作为开发者,拿到一个新的代码库的时候一般都会先去看README文件,通过这个文件可以知道这套代码所 ...
- 【leetcode 206】 反转链表(简单)
链表 概念: 区别于数组,链表中的元素不是存储在内存中连续的一片区域,链表中的数据存储在每一个称之为「结点」复合区域里,在每一个结点除了存储数据以外,还保存了到下一个结点的指针(Pointer). 由 ...
- Python的关键字参数与斜杠“/”
Python3.8 新增了一种语法,可以使用斜杠 / 占据一个参数的位置,表示在此之前的参数都只接受位置参数的传参形式. 例如,对以下函数声明: def func(a, b, /, c, d, *, ...
- cloudwu/coroutine 源码分析
1 与其它协程库使用对比 这个 C 协程库是云风(cloudwu) 写的,其接口风格与 Lua 协程类似,并且都是非对称 stackful 协程.这个是源代码中的示例: #include " ...
- NodeJS全栈开发利器:CabloyJS究竟是什么
CabloyJS CabloyJS是一款顶级NodeJS全栈业务开发框架, 基于KoaJS + EggJS + VueJS + Framework7 文档 官网 && 文档 演示 PC ...