目录

5.静态与命名控制

5.1静态数据成员

5.2静态成员函数

5.3静态对象

5.4类作用域及对象的生存期

5.5命名空间


5.静态与命名控制

通常,在函数体内定义一个变量时,每次函数调用时编译器会为这些内部变量分配内存。如果这个变量有一个初始化表达式,那么每当程序运行到此处,初始化就被执行。如果想在两次函数调用之间保留一个变量的值,通常的做法是定义一个全局变量来解决,但是这个变量就不仅仅受这个函数的控制,从某种程度上说存在不安全因素。为了解决这个问题,C和C++语言允许在函数内部创建一个static对象,这个对象存储在程序的静态数据区中,而不是堆栈中。这个对象只在函数第一次调用时初始化一次,以后它将在两次函数操作间保持它的值。

5.1静态数据成员

当声明一个类后,可以建立该类的多个对象,每个对象都有类中所定义的数据成员的拷贝,对应不同的存储空间,各个对象相互独立,实现了数据的封装与隐藏;但在有些情况下,类中的某一数据成员是该类所有对象所共有的,如果采用前面所述的数据成员的定义,则不能实现数据成员的共享。

例如建立一个学生成绩的线性表。设计学生类Student,抽象出的属性:姓名、学号、成绩;用构造函数实现学生的初始化,用成员函数Print实现输出每个学生信息和总人数。

Class Student
{
private:char *name;int stu_no;int total;float score;
public:Student(char *na, int no, float sco);void Print();
};

想要统计学生总人数,一种方法是将需要共享的数据成员定义成全局变量,但这样做将带来安全隐患,如破坏了类的封装特性、不利于信息隐藏等。另一种方法是在类中增加一个数据成员来存放总人数。但是这样做,在用Student定义对象时,每定义一个对象,则该对象中就会有存放总人数的数据成员的副本。而总人数对于所有对象来说是一样的。因此当总人数发生改变时,所有对象中存放总人数的数据成员都要同时改变,就需要在声明一个专门用来记录变化的总人数的成员函数。这样做很不方便,也容易造成数据的不一致。由于这个数据是所有对象所共享的,C++提供了静态数据成员来解决该类问题。类的静态数据成员拥有一块单独的存储区,不管用户创建了多少个该类的对象。所有这些对象的静态数据成员都共享这一块静态存储空间,这就为这些对象提供了一种互相通信的方法。因此可将该数据成员定义为静态数据成员。

静态数据成员的定义:
static 类型名 静态成员名;
静态数据成员的初始化:
类型 类名::静态数据成员 = 初始化值;
#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Student
{private:char *name;int stu_no;float score;static int total;public:Student(char *na, int no, float sco);void Print();
};int Student::total = 0;Student::Student(char *na, int no, float sco)
{name = new char[strlen(na)+1];strcpy(name, na);stu_no = no;score = sco;total++;
}void Student::Print()
{cout << "NO." << total << "student:" << name << setw(4) << stu_no << setw(4) << score <<endl;cout << "Total:" << total <<endl;
}int main()
{Student s1("zhangming", 1, 90);s1.Print();Student s2("wanglan", 2, 95);s2.Print();Student s3("yumin", 3, 87);s3.Print();return 0;
}

注意:

(1)静态数据成员声明时,加关键字static说明。

(2)静态数据成员的初始化应在类外声明并在对象生成之前进行,默认时,静态成员被初始化为零。

(3)静态数据成员在编译时创建并初始化。不能用构造函数进行初始化,静态数据成员不能在任何函数内分配存储空间和初始化。

(4)静态数据成员属于类,不属于任何一个对象,只能在类外通过类名对它进行访问。静态数据成员的访问形式是:

类名::静态数据成员;

(5)静态数据成员的主要用途是定义类的各个对象所公用的数据,如统计数据、平均数等。

(6)静态数据成员和普通数据成员一样,可以声明为public、private或protected。

静态数据成员的使用:

#include <iostream>
using namespace std;class A
{private:static int a;int b;public:A(int i, int j);void show();
};A::A(int i, int j)
{a = i;b = j;
}int A::a;void A::show()
{cout << "This is static a:" << a <<endl;cout << "This is non-static b:" << b <<endl;
}int main()
{A x(1, 1);x.show();A y(2, 2);y.show();x.show();return 0;
}

5.2静态成员函数

与静态数据成员一样,用户可以创建静态成员函数,方法是在成员函数名前用static修饰。静态成员函数是为了类的全体服务而不是为了一个类的部分对象服务,因此就不需要定义全局函数。当产生一个静态成员函数时,也就表达了与一个特定类的联系。静态成员函数不能访问一般的数据成员和成员函数,它只能访问静态数据成员和其他的静态成员函数。静态成员函数是独立于类对象而存在的,因此没有this指针。

定义静态成员函数:
static 返回类型 静态成员函数名(参数表);
调用方式:
类名::静态成员函数名(实参表);

静态成员函数应用举例:

#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Employee
{private:char *name;int number;static int total;public:Employee();static void Print();void Printinfo();
};int Employee::total = 0;Employee::Employee()
{name = new char[10];cout << "Input name and number:" <<endl;cin >> name >> number;total++;
}void Employee::Print()
{cout << endl << "Total:" << total <<endl;
}void Employee::Printinfo()
{cout << "Name:" << name << setw(7) << " " << "Number:" << number <<endl;
}int main()
{Employee::Print();int i;Employee s[3];cout <<endl;for(i = 0; i < 3; i++){s[i].Printinfo();}Employee::Print();return 0;
}

(1)采用静态成员函数,可以在创建对象之前处理静态数据成员(在不生成类对象的情况下,直接存取静态数据成员),这是普通成员函数不能实现的。

(2)静态成员函数在同一个类中只有一个成员函数的地址映射,节约了计算机系统的开销,提高了程序运行效率。

(3)静态成员函数可以在类内定义,也可以在类外定义,在类外定义时,不要用static前缀。

(4)静态数据成员可以被非静态成员函数引用,也可以被静态成员函数引用。但是静态成员函数不能直接访问类中的非静态成员。

例如:将Print()写成:

void Employee::Print()
{cout << “name:” << name << setw(7) << “no:” << number <<endl;cout << endl << “total:” << total <<endl;
}

一般而言,静态成员函数不能访问类中的非静态成员。若确实需要,静态成员函数只能通过对象名(或指向对象的指针)访问该对象的非静态成员。

在静态成员函数中访问非静态成员:

#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Employee
{private:char *name;int number;static int total;public:Employee();static void Printinfo(Employee);
};int Employee::total = 0;Employee::Employee()
{name = new char[10];cout << "Input name and number:" <<endl;cin >> name >> number;total++;
}void Employee::Printinfo(Employee a)
{cout <<endl << "Name:" << a.name << setw(7) << " " << "Number:" << a.number <<endl;cout << "Total:" << total <<endl;
}int main()
{int i;Employee s;cout <<endl;Employee::Printinfo(s);return 0;
}

(1)在静态成员函数Print()中通过对象s访问非静态成员name和number。

(2)静态成员函数不含this指针。

(3)如果程序中没有实例化的对象,则只能通过“类名::”访问静态成员函数;如果有实例化的对象,则既可以通过类名方式访问静态成员函数,也可以通过对象访问静态成员函数。但一般不建议用对象名来引用。

例如:
Class A
{
private:static int a;
public:A(int i);static void show();
};
A::A(int i)
{a = i;
}
void A::show()
{cout << “This is static a:” << a <<endl;
}
int A::a;
int main
{A x(1);x.show();     //对象访问静态成员函数A::show();    //通过类名访问静态成员函数return 0;
}

5.3静态对象

在定义对象时,可以定义类的静态对象。与静态变量一样,在定义对象时,且只是第一次是,才需要执行构造函数进行初始化。静态对象的析构函数是在main结束时才自动执行的。与普通对象相同,静态对象的析构函数的执行与构造函数执行的顺序相反。

静态对象的定义:

static 类名 静态对象名;

静态对象的构造函数、析构函数的执行:

#include <iostream>
using namespace std;class Obj
{private:char ch;public:Obj(char c);~Obj();
};void f();
void g();
Obj A('A');Obj::Obj(char c):ch(c)
{cout << "construct..." << ch <<endl;
}Obj::~Obj()
{cout << "destruct..." << ch <<endl;
}void f()
{static Obj B('B');
}
void g()
{static Obj C('C');
}int main()
{cout << "inside main()" <<endl;f();f();g();cout << "outside main()" <<endl;return 0;
}

对象A是一个全局的obj类的对象,因此在main函数之前就调用A的构造函数。执行函数f()时,在函数f()内部定义一个静态对象B,程序控制第一次转到该对象的定义点时,系统自动执行B的构造函数,而且只执行一次。因此第二次调用函数f()时,就不再执行对象B的构造函数了。静态对象的析构函数是在退出main函数时才被系统自动调用,而不是退出静态对象所在的函数。因此,在退出main函数时才执行局部静态对象B和C的析构函数。静态对象的析构函数也是只执行一次,而且是按他们初始化时相反的顺序进行的。因此本程序先执行对象C的析构函数,然后是对象B,最后是全局对象A。

5.4类作用域及对象的生存期

5.4.1类作用域

类作用域是指在类的定义中由一对花括号括起来的部分,包括数据成员和函数成员。在类所用域中声明的标识符,其作用域与标识符声明的顺序没有关系。类中的成员函数可以不受限制地访问本类的数据成员和其他成员函数;但是在该类的作用域外,不能直接访问类的数据成员和函数成员,即使是公有成员,也只能通过本类的对象才能访问。

类作用域应用:

#include <iostream>
using namespace std;class Test
{private:int a;public:void f1();void f2();
};void Test::f1()
{f2();cout << a <<endl;
}void Test::f2()
{a = 100;
}int main()
{Test t1;t1.f2();t1.f1();return 0;
}

5.4.2对象的生存期

类对象的生存期是指对象从被创建开始到生存期结束为止的时间,类对象在声明时被创建,在释放时被终止。

(1)局部对象:是指定义在一个程序块或函数体内的对象。定义对象时,系统自动调用构造函数创建对象,程序运行结束时调用析构函数释放对象。

(2)静态对象:作用域从定义时开始到程序结束时止。程序第一次执行静态对象定义时,自动调用构造函数创建对象,程序运行结束时调用析构函数释放对象。

(3)全局对象:作用域是从定义时开始到程序结束时止。定义对象时,自动调用构造函数创建对象,程序运行结束时调用析构函数释放对象。

(4)动态对象:执行运算符new时创建动态对象,执行运算符delete时释放动态对象。

对象生存期应用:

#include <iostream>
using namespace std;class Obj
{private:char ch;public:Obj(char c);~Obj();
};void f();
void g();
Obj A('A');Obj::Obj(char c):ch(c)
{cout << "construct..." << ch <<endl;
}Obj::~Obj()
{cout << "destruct..." << ch <<endl;
}void f()
{static Obj B('B');Obj C('C');
}
void g()
{static Obj C('C');
}int main()
{cout << "inside main()" <<endl;f();f();cout << "outside main()" <<endl;return 0;
}

5.5命名空间

如果在一个C++程序中出现两个变量、函数或类名完全相同,就会产生冲突。解决命名冲突的办法有两个,一个是使用不同的标识符名,但有时会降低程序的可读性;另一个是使用命名空间,使用无.h扩展名的C++头文件,并指定std命名空间。

例如:
#include <iostream>
using namespace std;

using是一个在代码编译之前处理的指令。namespace为命名空间,它是ANSI/ISO C++的一个新特性,使用命名空间可以帮助开发人员在开发新的软件组件时不会与已有的软件组件产生标识符命名冲突,从而解决在程序中同名标识存在的潜在危机。

5.5.1命名空间的定义

所谓命名空间,是一种将程序库名称封装起来的方法。命名空间是C++为解决变量、函数名和类名等标识符的命名冲突服务的,它将变量等标识符定义在一个不同名的命名空间中。

namespace 命名空间标识符名
{成员的声明;
}

其中,命名空间标识符名在所定义的域中必须是唯一的,命名空间内的成员既包括变量,也包括函数。命名空间成员的访问方式:

命名空间标识符名::成员名

如在C++标准程序库中,使用了命名空间std。在使用C++标准程序库的任何标识符时,可以直接指定标识符所属的命名空间。

例如:
#include <iostream>
using namespace std;
int main()
{std::cout << “命名空间” <<endl;return 0;
}

命名空间的应用:

#include <iostream>
#include <string>
#include <string.h>
using namespace std;namespace A
{char user_name[] = "namespace A";void showname(){cout << user_name <<endl;}
}namespace B
{string user_name = "namespace B";void showname(){cout << user_name <<endl;}
}int main()
{A::showname();B::showname();strcpy(A::user_name, "good");A::showname();return 0;
}

using namespace std;编译指示在本程序中可以使用C++标准库中定义的名字。std是标准命名空间名,在标准头文件中声明的函数、对象和类模板,都在明明空间std中声明。程序中在命名空间A和B中定义了同名的字符串标识符user_name和函数showname(),但是因为是隶属于不同的命名空间,所以同名的字符串标识符user_name和函数showname()互不干扰。在使用命名空间时,可以通过预编译指示使用命名空间。

修改上例主程序:

Int main()
{using namespace A;showname();B::showname();strcpy(user_name, “good”);cout << user_name <<endl;return 0;
}

访问命名空间A的变量和函数时可以不显示使用命名空间A的标识符,但是访问命名空间B的变量和函数时,仍需要使用命名空间标识符访问其变量和函数。关于赋值,定义两个string类型的变量,并在定义的时候进行赋值。两个string类型的变量可以用”+”实现连接,并赋值给同类型的变量。但是把string类型的对象直接赋值给C风格的字符串的话,编译器就会报错。

例如:string var = “Olympic”;
char *p = var;         //错误
char *p = (char*)var   //正确

C++学习笔记:(三)静态与名字空间相关推荐

  1. UE4学习笔记#三、蓝图混合空间

    UE4学习笔记(谌嘉诚大佬的PUBG教程) 三.蓝图混合空间 1. 设置初始角色(关卡开始时pawn获得的角色) 2. 角色移动蓝图 3. 镜头方向蓝图 4. 利用TimeLine平滑切换行走跑步 5 ...

  2. SurfaceFlinger学习笔记(三)之SurfaceFlinger进程

    概述 本系列是基于android Q 即android10 SurfaceFlinger学习笔记(一)应用启动流程 SurfaceFlinger学习笔记(二)之Surface SurfaceFling ...

  3. MySQL学习笔记(三)查询

    写在前面:本篇为作者自学总结,学习内容为课堂所学和网络学习笔记汇总,对于内容引用部分在文中和文末注明. 文章仅供参考,如需深入了解,请查阅MySQL参考手册.附上下载链接: 链接:https://pa ...

  4. 数组存储与指针学习笔记(三)指针与数组

    嵌入式C语言学习进阶系列文章 GUN C编译器拓展语法学习笔记(一)GNU C特殊语法部分详解 GUN C编译器拓展语法学习笔记(二)属性声明 GUN C编译器拓展语法学习笔记(三)内联函数.内建函数 ...

  5. J2EE学习笔记三:EJB基础概念和知识 收藏

    J2EE学习笔记三:EJB基础概念和知识 收藏 EJB正是J2EE的旗舰技术,因此俺直接跳到这一章来了,前面的几章都是讲Servlet和JSP以及JDBC的,俺都懂一些.那么EJB和通常我们所说的Ja ...

  6. ROS学习笔记三:创建ROS软件包

    ,# ROS学习笔记三:创建ROS软件包 catkin软件包的组成 一个软件包必须满足如下条件才能被称之为catkin软件包: 这个软件包必须包含一个catkin编译文件package.xml(man ...

  7. python3常用模块_Python学习笔记三(常用模块)

    Python 学习笔记三 (常用模块) 1.os模块 os模块包装了不同操作系统的通用接口,使用户在不同操作系统下,可以使用相同的函数接口,返回相同结构的结果. os.name:返回当前操作系统名称( ...

  8. K8S 学习笔记三 核心技术 Helm nfs prometheus grafana 高可用集群部署 容器部署流程

    K8S 学习笔记三 核心技术 2.13 Helm 2.13.1 Helm 引入 2.13.2 使用 Helm 可以解决哪些问题 2.13.3 Helm 概述 2.13.4 Helm 的 3 个重要概念 ...

  9. 【AngularJs学习笔记三】Grunt任务管理器

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...

最新文章

  1. Python 十六进制转Base64_马克的Python学习笔记#数据编码与处理 4
  2. his系统oracle多少钱,医院信息系统怎样为医改调价做准备:HIS费用功能应用情况调查告诉您...
  3. e站app改内置hosts_米家踢脚线电暖器E评测:符合现代家居审美 全屋取暖“小钢炮”...
  4. jenkins ssh 远程部署_Jenkins部署jar到远程服务器
  5. 利用WPF建立自己的3d gis软件(非axhost方式)(九)SDK自带部分面板的调用
  6. 前端学到什么程度可以找到工作?
  7. react 怎么获取表格_React之表格操作
  8. Hadoop去掉格,换行符,制表符,回车符,换页符【好吧,其实用正则表达式一下子就搞定了】
  9. zoj 3351 Bloodsucker
  10. 一个比较牛逼人的博客
  11. 未来网络什么样?新华三是这么说的
  12. uniapp ->video 黑屏 无时长(新手容易遇到的坑)
  13. http_proxy设置
  14. ls一1测距仪说明书_生产力小工具 篇一:激光测距靠谱吗?杜克LS-1激光测距仪开箱测评...
  15. Kinect动作捕捉的改进
  16. 计算机科学与技术优劣,计算机科学与技术专业优势多多
  17. 如何在Xcode里面运行C语言程序---简单展示
  18. oracle导入 不是dba,IMP-00013: 只有 DBA 才能导入由其他 DBA 导出的文件
  19. STM32 使用SYN6288语音模块
  20. 西安电子科技大学李锦峰教授 计算机,李锦峰医生预约挂号-出诊时间-西安交通大学口腔医院(西安交大口腔医院)口腔颌面外科 - 名医汇...

热门文章

  1. 关于寻路算法的一些思考(1):A*算法介绍
  2. SQLLite (四):sqlite3_prepare_v2,sqlite3_step
  3. PyCairo 中的文本
  4. 容器网络|深入理解Cilium
  5. JDK中这些常用方法也有Bug
  6. e.printStackTrace() 会导致锁死?
  7. Linux线程(六)
  8. 【全真互联网下音视频技术创新应用】
  9. 当一百万名记者都嚷嚷着“Facebook 很糟糕”......
  10. Netflix选择AVIF作为下一代图片压缩技术