学习了博主的《漫谈继承技术》系列博文之后,相信大家都有所收获吧!这次博主将和大家一起探讨 《灵活而奇特的C++语言特性》 ,主要包括引用、常量(const)、常量表达式(constexpr)、静态(static)、外部(expert)、类型定义(typedef)、类型别名(aliases)、类型转换、作用域解析、统一初始化、显示转换运算符、特性(attribute)、用户自定义文本、头文件、可变长度参数列表和预处理器宏。尽管这个知识清单显得有点凌乱,但是这些话题都是博主经过精心挑选,是容易混淆的语言特性。本篇我们来学习一下作用域解析的观念以及使用场景,增进大家对《灵活而奇特的C++语言特性》的理解。
C++程序猿必须熟悉作用域(scope)的概念。程序中的所有名称,包括变量、函数和类名,都具有某种作用域。可以使用名称空间、函数定义、花括号界定的块和类定义创建作用域。当试图访问某个变量、函数或者类时,首先在最近作用域中查找这个名称,然后是相邻的作用域,依次类推,知道全局作用域。任何不在有名名称空间、函数、花括号界定的块和类中的名称都被认为在全局作用域中。如果在全局作用域中也找不到这个名称,编译器会给出一个未定义符号错误。那什么是作用域运算符呢?作用域运算符就是“::”。下面就让我们一起结合类的静态成员来探讨一下作用域运算符的使用。
类的静态成员
类的静态成员可以实现对象与对象之间的数据共享。类属性是描述类的所有对象的共同特征的数据项,对于任何对象实例,它的属性值是相同的。静态数据成员具有静态生存期,可以通过“类名::静态数据成员名”来访问。在类的定义中仅仅对静态数据成员进行引用性声明,必须在命名空间作用域的某个地方使用类名限定定义性声明,这时也可以进行初始化。静态函数成员既可以在类定义中实现,也可以先在类中声明,再在类定义外实现,但static关键字只能写在类定义中,否则编译出错。
提示:之所以类的静态数据成员需要在类定义之外再加以定义,是因为需要以这种方式专门为它们分配空间。非静态数据成员无须以此方式定义,因为它们的空间是与它们所属对象的空间同时分配的。
静态成员函数可以通过类名或对象名来调用,而非静态成员函数只能通过对象名来调用。
习惯:虽然静态成员函数可以通过类名和对象名两种方式调用,但一般习惯于通过类名调用。因为即使通过对象名来调用,起作用的也只是对象的类型信息,与所使用的具体对象毫无关系。
在静态成员函数中访问非静态成员需要指明对象,因为对静态成员函数的调用是没有目的对象的,因此不能像非静态成员函数那样,隐含的通过目的对象访问类的非静态成员。
在静态成员函数中只能访问静态成员,但是在非静态成员函数中可以访问静态成员。静态数据成员可以定为const,但静态成员函数不能定义为const,否则编译出错。在对类的静态数据成员(包括公有数据成员和私有数据成员)初始化的同时,还可以引用类的其他私有成员。咱们来举个栗子吧。
#include <iostream>
using namespacestd;
class Point
{
public:
//通过静态成员函数访问静态数据成员m_pPtr,返回其属性m_nValue
staticintgetpPtr();
//通过静态成员函数访问静态数据成员m_P,返回其属性m_nValue
staticintgetP();
//通过静态成员函数释放静态数据成员m_pPtr在堆上的空间
staticvoidDestroy();
//析构函数
virtual~Point();
private:
//私有构造函数声明
Point(intnValue = 0);
intm_nValue; //数据成员m_nValue
//静态数据成员m_pPtr,类的所有对象共享一份
staticPoint *m_pPtr;
//静态数据成员m_P,类的所有对象共享一份
staticPoint m_P;
};
//引用私有构造函数,对其进行初始化
Point *Point::m_pPtr= new Point(6);
//引用私有构造函数,对其进行初始化
Point Point::m_P= Point(8);
//私有构造函数实现
Point::Point(intnValue/*= 0*/)
:m_nValue(nValue)
{
cout<< "构造函数" << endl;
}
inline Point::~Point()
{
cout<< "析构函数" << endl;
}
inline intPoint::getpPtr()
{
//m_pPtr是指针,通过"->"来访问
returnm_pPtr->m_nValue;
}
inline intPoint::getP()
{
//m_P是对象,通过"."来访问
returnm_P.m_nValue;
}
void Point::Destroy()
{
if(nullptr!= m_pPtr)
{
//释放静态成员
deletem_pPtr;
//避免野指针
m_pPtr= nullptr;
}
}
int main(intargc,char**argv)
{
//通过类名::(作用域运算符)访问类的静态方法
cout<< Point::getpPtr() << endl;//6
cout<< Point::getP() << endl;//8
//显式的释放m_pPtr的空间
Point::Destroy();
//计算类对象的大小
//在32位系统上,sizeof(Point)值为8,虚表指针和m_nValue各占4个字节
cout<< sizeof(Point)<< endl;
return0;
}
程序运行结果:
博主结合了作用域、可见性已经类的静态成员和大家一起探讨了作用域解析。作用域解析运算符(::)单独使用的场景只有一处,就是在访问全局命名空间中的名称时候,其他都是结合着其他名称来使用的,例如using 命名空间名称::名称、类名称::静态成员等。关于作用域解析我们就聊到这里了,相信大家都有所收获。
如果想了解更多关于C++语言特性相关的知识,请关注博主《灵活而奇特的C++语言特性》系列博文,相信你能够在那里寻找到更多有助你快速成长和加深你对C++语言特性相关的知识和一些特性的理解和掌握。当然,如果你想了解关于继承方面的技术,请关注博主《漫谈继承技术》系列博文。