常变量、常对象、常引用、指向常对象或常变量的指针等在定义时都使用了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++类中的常成员和静态成员的更多相关文章

  1. cc31a_demo--CppPrimer_静态成员与继承-在派生类中访问基类中的static成员的方法

    //*基类中的static成员,在整个继承层次中只有一个实例 //*在派生类中访问基类中的static成员的方法 //1.基类名::成员名 //2.子类名::成员名 //3.对象.成员名 //4.指针 ...

  2. 在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static

    在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static ! 在C语言中,我们使用pthread_create ...

  3. C++ 类中的static成员的初始化和特点

    C++ 类中的static成员的初始化和特点 #include <iostream> using namespace std; class Test { public: Test() : ...

  4. 类中的internal成员可能是一种坏味道

    前言 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原先只是有一丝细微的感觉,而现在将它和一些其他的方面进行了联 ...

  5. C++ 类中特殊的成员变量(常变量、引用、静态)的初始化方法

    有些成员变量的数据类型比较特别,它们的初始化方式也和普通数据类型的成员变量有所不同.这些特殊的类型的成员变量包括: a.引用 b.常量 c.静态 d.静态常量(整型) e.静态常量(非整型) 常量和引 ...

  6. C/C++中的常成员函数

    代码: #include <iostream> using namespace std; class A{ public: void func1(){ cout<<" ...

  7. c++:类中的static成员

    首先静态成员可以是public的,也可以是private的,只需在一般的变量.函数声明语句前加上static关键字即可声明一个static变量. 类中的静态成员存在与任何对象之外,所有该类对象的共享一 ...

  8. [转]C++ 类中的static成员的初始化和特点

    在C++的类中有些成员变量初始化和一般数据类型的成员变量有所不同.以下测试编译环境为: ➜ g++ -v Using built-in specs. COLLECT_GCC=g++ Target: x ...

  9. 【c++】类中的const成员

    const成员变量 举个例子 #include <iostream> using namespace std; class A { public: A(int size) : SIZE(s ...

随机推荐

  1. ArrayLIst在指定位置插入的内部实现

    今天看到一个问题:ArrayList的add方法有两种使用,那么add到指定位置内部是怎么实现的? 发现自己对这块地方不熟悉,所以立马去看了ArrayList下的源码 // 第一个 public bo ...

  2. dfs深搜

    一.01背包dfs //回溯法,01背包 #include<iostream> #include<algorithm> using namespace std; const i ...

  3. Prometheus监控实战应用

    目录 1.Prometheus的主要特征及概述 2.普罗米修斯原理架构图 3.下载安装启动Prometheus 4.web客户端操作 5.默认图像 6.目录解释 7.配置文件 8.监控指标 8.1.监 ...

  4. 这些 Shell 分析服务器日志命令集锦,收藏好

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 自己的小网站跑在阿里云的ECS上面,偶尔也去分析分析自己网站服务器日志,看看网站的访问量.看看 ...

  5. 不可不知的 MySQL 升级利器及 5.7 升级到 8.0 的注意事项

    数据库升级,是一项让人喜忧参半的工程.喜的是,通过升级,可以享受新版本带来的新特性及性能提升.忧的是,新版本可能与老的版本不兼容,不兼容主要体现在以下三方面: 语法不兼容. 语义不兼容.同一个SQL, ...

  6. KeyDB重量发布6.3.0开源版

    摘要:5月12日 KeyDB 社区隆重发布了 6.3.0开源版本,将与华为加拿大研究院DCS团队2021-2022年合作的成果,深度优化的企业版的能力贡献给了开源社区. KeyDB是目前Redis 分 ...

  7. 构建AR视频空间大数据平台(物联网及工业互联网、视频、AI场景识别)

    目       录 1.      应用背景... 2 2.      系统框架... 2 3.      AI场景识别算法和硬件... 3 4.      AR视频空间管理系统... 5 5.    ...

  8. Nexus5x 修改Android开机动画

    1.制作帧动画 这里随便从网上找了一个gif图片,导入PS中,打开后会形成很多帧图层,选择导航栏中的文件->脚本->将图层导出到文件可以将所有图层导出来.要注意文件命名,Android会按 ...

  9. 在linux上开启酸酸乳,未完待续

    在服务器调试深度学习环境的时候总需要下载conda的包,一直以来都觉得是因为国内访问慢,于是想在服务器上开 ,或者ssr.由于过去用ssr多一些,于是想了解ssr on linux. 1.首先win1 ...

  10. 基于bat脚本的前端发布流程设计与实现

    写在前面 本文大致向读者介绍了楼下几点知识,希望在编写bat脚本时候能够帮到读者,如果能够有所启迪,那就更好了. bat脚本的相关知识和案例编写 用windows自带的命令压缩文件 windows和l ...