1、变量和对象定义在不同的位置
(函数体内、类体内、函数圆形参数表、所有函数和类之外)其作用域、可见性、生命周期都不同。
属于整个类的数据成员--静态数据成员
用于处理静态数据成员的函数---静态成员函数
问题:如果每次访问私有成员的时候,如果每次都去调用公有接口函数进行访问的话,开销会特别大,会影响运行效率。
这个时候又更好的方法进行访问:在类中声明这个类之外有哪些类、哪些函数是本类的“朋友”。——友元
友元:对一些类外的函数、其他的类,给预授权,使之可以访问类的私有成员。可以提高访问效率,但不可避免带来安全性的问题(所有数据共享都会面临的一个问题:共享的数据如何保证其安全性)。可以通过const关键字,限制对共享数据的修改。
2、标识符的作用于与可见性
作用域:
从小到大有:函数原型作用域、局部作用域(块作用域)、类作用域、文件作用域、命名空间作用域(以后会有介绍)
函数原型作用域:函数原型中的参数,始于“(”,并且结束于“)”,所以说,声明函数的时候可以只给出参数的类型而不用给出参数名字。如下:注意区分定义的局部作用域。声明和定义不同
局部作用域:局部是指函数体的一对大括号{……}之内。同时也可以在{……}之内再使用{……},也是局部作用域。函数的形参,在块声明中的标识符;作用域自声明处起,限于块中。
类作用域:类的成员具有类作用域,其范围包括类体和成员函数体。
在类的作用域以外访问类的成员:a、静态成员:通过类名,或者该类的对象名、对象引用访问。
b、非静态成员:通过类名,或者该类的对象名、对象引用、对象指针访问。
文件作用域:不在前述的各个作用域中刚出现的声明,就具有文件作用域。其作用域开始于声明点,结束于文件尾。文件作用于也称为静态作用域。
可见性:可见性是对标识符的引用的角度来谈的概念。可见性表示从内层作用域向外层作用域“看”时能看见什么。如果标识在某处可见,就可以在该处引用此标识符。
如果某个标识符在外层中声明,且在内层中没有同一个标识符的声明,则该标识符在内层中可见。对于两个嵌套的作用域,如果在内层中声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层中不可见。
3、对象的生存期:静态生存期,动态生存期。
静态生存期:生存周期与程序的运行期相同,在文件作用域中声明的对象具有这种生存期。在函数内部声明静态生存期对象要使用stastic关键字。
静态局部变量:局部可见,全局寿命。比如,void f(){static a=2;},局部变量没有初始化时默认值为0
动态生存期:开始于程序执行到声明点时,结束于命名该标识符的作用域结束处。块作用域中声明的,没有用stastic修饰的对象是动态生存期的对象(习惯称为局部生存期对象)。
注意两个static int a 的作用域不一样,虽然都是全局寿命。
4、类的静态数据成员
用关键字static声明,为该类的所有对象所共享,静态数据成员具有静态生存期。必须在类外定义和初始化,用(::)来指明所属的类。
静态函数成员主要用于处理该类的静态数据。普通成员函数和静态成员函数的区别是:普通成员函数在参数传递时编译器会隐藏地传递一个this指针.通过this指针来确定调用类产生的哪个对象;但是静态成员函数没有this指针,不知道应该访问哪个对象中的数据,所以在程序中不可以用静态成员函数访问类中的普通变量.。总之:类中如果需要静态数据成员,那么我们就应该定义静态函数成员其处理它。
例子:Point的类图如下
例子:
#include
using namespace std;
class Point
{
public:
Point(int x, int y);
Point(Point &p) {
x = p.x;
y = p.y;
count++;
}
int getX() { return x; }
int getY() { return y; }
static void showCount() { //值处理静态成员
cout << "Object count=" << count << endl;
}
~Point() {
count--;
}
private:
int x, y;
static int count;
};
Point::Point(int x, int y):x(x), y(y) {
count++;
}
int Point::count = 0;
int main() {
Point::showCount();
Point a(4, 5);
//cout << "Point A:" << a.getX << "," << a.getY() << endl; //忘加()报错“Point::getX”: 非标准语法;请使用 "&" 来创建指向成员的指针
cout << "Point A:" <
疑问:为什么加了static的静态函数只能访问静态数据?
参考:静态成员、静态对象使用
5、类的友元:friend ,类体中声明,类外的哪些函数、类是他的朋友,使之获得访问私有成员的授权。同时友元是c++中一种破坏封装和隐藏的机制。而且是单向的
为什么要使用友元?----------有时需要在封装和效率之间做取舍。
通过将一个模块声明为另外一个模块的友元,一个模块能够引用另外一个模块中本是被隐藏的信息。可以声明友元函数和友元类,为了确保数据的完整性,及数据封装与隐藏的原则,要慎用友元。
友元函数:是在类声明中,由关键字friend修饰说明的非成员函数,在他的函数体中能够通过对象名访问private和protected成员。作用是可以增加灵活性,使程序员可以在封装和快速性之间做出合理选择。访问对象的成员必须通过对象名
#include
#include
using namespace std;
class Point
{
public:
Point(int x = 0, int y = 0):x(x),y(y){}
~Point();
//传对象的引用(别名)是为了提高效率,传整个对象的话时间和空间开销大。但会带来双向传递的问题,可能会改变原始数据
friend float dist(Point &a, Point &b);
private:
int x, y;
};
Point::~Point()
{
}
float dist(Point &a, Point &b) {
double x = a.x - b.x;
double y = a.y - b.y;
return static_cast(sqrt(x * x + y * y));
}
int main() {
Point p1(1, 2), p2(5, 5);
cout << "distance :" << dist(p1, p2) << endl;
return 0;
}
友元类:
类的友元是单向的,声明B类时A类的友元不等于A类也是B类的友元。
6、共享数据的保护:-------定义为常类型const
常类型有:常对象,常成员,常引用,还有常数组,常指针。
常对象:必须进行初始化,不能再被更新。const 类名 对象名;
常成员:用const进行修饰的类成员:常数据成员和常函数成员。
常引用:被引用对象不能被更新。const 类型说明符 &引用名。
常数组:数组原始不能被更新。 类型说明符 const 数组名[大小]
常指针:指向常量的指针。
常成员函数:类型说明符 函数名(参数表) const,这里的const是函数类型的一个组成部分,因此在实现部分也要带const关键字。同时const关键字可以被用于参与对重载函数的区分。通过常对象只能调用常成员函数。
常函数意味着不会改变成员状态。
常引用意味着只读。。只想要高效率的特点而不希望双向传递。
7、多文件结构和编译预处理命令
c++程序的一般结构:一个工程可以划分为多个源文件,例如:
a、类声明文件(.h文件)
b、类实现文件(.cpp文件)
c、类的使用文件(main()所在的.cpp文件)。可以利用工程来组合各个文件。
系统提供的头文件一般不带.h,而自己写的一般都要带.h,导入的时候要用双引号".h"来使用。使用<>会直接到安装目录下去找相应的文件,而使用" "的时候回在当前工作目录下寻找对应的文件,如果找不到才会到安装目录下找。
工程文件下,整个编译和连接的过程如下:
实质过程:编译完成后会形成.obj的文件,它不是可执行程序,想要生成可执行程序还需要连接过程:连接过程是将生成的.obj文件连接在一起,以及将系统运行库里的内容也连接到一起,最后形成可执行文件。
如果要使用在同一个工程中,但是在别处定义(不是当前程序中)的变量时,就可以把它当做外部变量来使用。在文件作用域中的变量就是默认恶意作为外部变量来使用的。
外部变量:除了定义它的源文件中可以使用之外,还能被其他文件来使用。文件作用域中定义的变量,默认情况下都是外部变量;在其他文件中如果需要使用,需要用extern关键字来声明。
另外:函数定义的位置和调用的位置可能不在统一文件中,这时如果想使用这个函数,就是使用外部函数。
外部函数:在所有类之外声明的函数(也就是分成员函数)都是具有文件作用域的,这样的函数都可以在不同的编译单元中被调用,只要在调用之前进行引用性声明(即声明函数原型)即可。
有事我们并不希望在当前文件中定义的标识符被拿到别的文件中使用。就需要把它限制在同一个命名空间中。
将变量和函数限制在编译单元内:在匿名命名空间中定义的变量和函数,都不会暴露给其他的编译单元。
namespace{//匿名的命名空间
int n;
void f(){n++;} }
标准C++库:
标准C++类库是一个极为灵活并可扩展的课重用软件模块的集合。标准C++类与组件在逻辑上分为6中类型:
输入/输出类;容器类与抽象数据类型;存储管理类;算法(如查找、排序等);错误处理;运行环境支持。
编译预处理命令:
条件编译指令:#if和#endif
#if 常量表达式//当“常量表达式”非0时编译
程序正文
#endif
以及第二种表示:#if 常量表达式//当“常量表达式”非0时编译
程序正文1
#else
//当“常量表达式”0时编译
程序正文2
#endif
或者是更多的条件选择:可以根据需要来设计多种选择。
另外还有判断标识符是否(主要是在头文件中)被定义过:避免重复定义
实验部分:明天补。。。