/*
*一、标识符的作用域和可见性
* (一)作用域
* 作用域是一个标识符在程序正文中有效的区域
* c++中标识符(名字)的作用域有函数作用域、局部作用域(块作用域)、类作用域、命名空间作用域
* 1、函数作用域
* 是c++程序中最小的作用域
* 函数原型声明时形参的作用范围解释函数原型作用域
* 函数原型中,标识符没有作用可以省略不写,但是在函数声明时要给出形参标识符
* 2、局部作用域
* 在函数的部分区域有效,具有局部作用域的变量也称为局部变量
* 3、类作用域
* 类可以被看成一组有名成员的集合,类X对成员m具有类作用域,对m的访问方式有以下三种
* (1)如果x成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员M也就是说m在这样的函数中都起作用
* (2)通过表达式x.m或者X::m,这是程序中访问对象成员的最基本的方法
* (3)通过指针,ptr->m
* 4、命名空间作用域
* (1)命名空间
* 一个大的程序由不同模块构成,不同模块中的类和函数之间有可能发生重名,这样就会引起错误,使用命名空间就会消除这个问题
* namespace 命名空间{
* 命名空间内的各种声明(函数声明、类声明、……)
}
* 一个命名空间确定一个命名空间作用域,在该命名空间内声明的,不属于之前各个作用域的标识符,都属于命名空间作用域
* 命名空间内部可以直接引用当前命名空间中声明的标识符,如果要引用其他命名空间的标识符,则需要
* 命名空间名::表示名符
* 命名空间允许镶套使用
* namespace OuterNs{//命名空间
* namespace InnerNs{//命名空间
* class someclass{}//类
}
}
* (2)using语句
* 但是这一命名方式过于冗杂,于是又提供了using语句
* using 命名空间::标识符//可以在当前作用域引用这个标识符
* using namespace 命名空间//在当前作用域,可以引用该命名空间的所有标识符
* c++标准程序库的所有标识符都在std命名空间内
* 如果去掉using namespace std,则引用需要使用:std::cin; std::cout;等
* 5、实例(看代码)
* (二)可见性
标识符的可见范围就是标识符的可见性,程序运行到某一点,能够引用的标识符,就是该处可见的标识符
* 标识符可见性的一般规则如下:
* 标识符要声明在前,引用在后
* 同一作用域,不能声明同名的标识符
* 在没有互相包含关系的作用域中声明了同名的标识符,互不影响
* 在两个或者两个以上具有包含关系的作用域中声明了同名标识符,则外层在内层不可见
* 二、对象的生存周期
* 对象从诞生到结束的这段时间就是它的而生存周期,在生存周期内,对象将一直保它的状态,变量也将保持它的值不变一直到数据更新为止
* (一)静态生存期
* 如果对象的生存期与程序的运行期相同,则称它 具有静态生存期
* 在命名空间作用域中声明的对象都具有静态生存期,如果要在函数内部的局部作用域中声明静态生存期的队形,则要使用关键字static
* static int i;//i是具有静态生存期的变量,也称为静态变量
* 局部作用域中静态变量的特点:
* 不会随着每次函数的调用而产生一个副本,也不会随着函数的返回而失效,也就是说,在下次调用时,,它依旧保持上一回的值
* 即使发生了递归调用,也不会为该变量创建新的副本,每次调用共享
* 在定义静态变量的同时,也可以为他赋初值
* static int i=5;
* (二)动态生存期
* 除了命名空间中声明的对象和用关键字static定义的变量外,其余的对象都具有动态生存周期
* 在局部作用域中声明具有动态生存周期的对象,习惯上也称为局部生存期对象,局部生存期对象诞生于声明点,结束与所在块执行完毕时
* 三、类的静态成员
* 模块间的而数据共享是通过函数与函数之间的数据共享来实现的,包括两个途径——参数传递和全局变量
* 静态成员是解决同一个类的不同对象之间数据、函数共享问题的
* (一)静态数据成员
* 如果某一个属性为整个类所共有的,不属于哪一个具体的对象,则采用static关键字来声明静态成员
* 静态成员在每一个类只有一个副本,由该类的所有对象共同维护和使用,从而事项了一个类不同对象之间的数据共享
* 类属性是描述类的所有对象的共同数据的一个数据项,对于任何对象实例,它的属性值是相同的
* 静态数据成员具有静态生存周期,因为静态数据成员不属于任何一个对象,所以要用类名对他进行访问
* 类名::标识符
* 在类的定义中仅仅对静态成员进行引用性声明,必须在空间作用域的某个地方是由类名限定定义声明,这时可以进行初始化(以这种方式专门为他们分配空间)
* UML中,数据成员下划线表示静态成员
* (二)静态函数成员
* 静态成员函数也属于整个类,由同一个类的所有对象所共有,为这些对象所共享
* 静态成员函数可以直接访问该类的静态数据和函数成员,而访问非静态成员,必须通过对象名
* 静态成员函数主要用来访问同一个类中的静态数据成员,维护对象之间共享的数据
* uml中静态函数成员前面加<<static>>来表示
* 四、类的友元
* 友元关系提供了不同类或对象的成员函数之间、类的成员函数与御坂函数之间进行的共享的机制
* 一个类主动声明了那些类、那些函数是他的朋友从而进行给他们提供对本类的访问特许
* 在一个类中,可以用关键字friend将其他函数或者类的声明为友元
* (一)友元函数
* 友元函数是在类中用关键字friend修饰的非成员函数,可以是一个普通的函数,也可以是其他类的成员函数
* 可以通过对象名访问类的私有和保护成员
* uml中函数前面加<<friend>>来表示
* (二)友元类
* 若a是b的友元类,则A的所有函数都是B的友元函数都可以访问B的私有和保护成员
* class b{
…… //B类的成员声明
friend A ;//声明A是B的友元类
……
}
* 注意:1、有缘不能传递
* A是B的友元,B是C的友元,在没有声明A和C的关系时,不存在友元关系
* 友元关系是单向的
* 友元关系是不被继承的
* 五、共享数据的保护
* 对于既需要共享又需要防止改变的数据应该声明为常量
* (一)常对象
* 它的数据成员值在整个生存周期内不能被改变,也就是说,常对象必须进行初始化,并且不能被更新
* const 类型说明符 对象名
* 常对象的值不能够被改变
* 常量可以被初始化,但是不能进行赋值
* (二)用const修饰的类成员
* 1、常成员函数
* 使用const关键字修饰的函数为常成员函数
* 类型说明符 函数名(参数表)const
* 注意:1、const是函数类型的一个组成部分,因此在函数定义部分也要加const
* 2、如果将一个对象设置为常函数,纳米通过该常函数只能调用它的常成员函数,不能调用其他成员函数
* 3、无论是否通过常对象调用常成员函数,在常成员函数调用期间,目的对象都被视为常对象,因此常成员函数不能更新目的对象的数据成员
* 4、const关键字可以用于函数重载
* void c();
* void c()const;、
* 是对c函数的有效重载
* 对于不需要改变状态的成员函数都应该使用const关键字
* 2、常数据成员
* 一个类中如果说明了常数据成员,那么任何函数中都不能对该成员赋值,构造函数对该数据成员赋值,只能通过初始化列表、
* 类成员中的静态变量和常量都应该在类定义之外加以定义,但是类的静态常量如果具有整数类型和枚举类型,可以在类中直接定义它为常量值
* 3、常引用
* 如果在声明引用时,用const修饰,被声明的而引用就是常引用,常引用所引用的对象不能更新,如果常引用作为形参,那么它引用的实参将不会发生改变
* 常引用形式
* const 类型说明符 &引用名
* 非const的引用只能绑定到普通对象上,不能绑定到常对象,但是常引用可以绑定到常对象上
* 通过常引用访问绑定对象时,只能把对象当作常对象,
* 对于基本数据成员,不能为数据赋值、对于类类型的引用,不能修改它的数据成员,也不能调用它不是const类型的函数成员
*
* 六、多文件结构和编译预处理命令
* (一)C++的一般组织结构
* 三部分:类的定义、类的成员的实现、主函数
* 一个类的定义必须出现在所有使用该类的编译单元中,将类的定义写在头文件中,使用该类的编译单元则包含这个头文件
* 一个项目通常分为3个文件:类定义文件(*.h)类实现文件(*.cpp)和类的使用文件(*.cpp,主函数文件)
* 比较复杂的,每一个类都有单独的定义和实现文件,这样,可以对不同的文件单独进行编写、编译,最后的连接
* #include<>一般是比较标准的文件
* #include""一般自己写的用这种
* #include属于预处理文件
* 决定文件是放在源文件还是头文件:
* 需要分配空间的定义放在源文件中
* 内联函数比较特殊,它需要嵌入到每一个调用它的函数中,所以应该放在头文件中
* (二)外部变量和外部函数
* 1、外部变量
* 一个变量除了在定义它的源文件中使用还可以在别的源文件中使用,这样的变量称为外部变量,命名作用域中定义的变量,默认情况下都是外部变量
* 在其他文件中需要引用这一变量,需要加关键字extern
* 对外部变量的声明可以是定义性声明(分配内存空间,初始化)也可以是引用性声明(引用定义在别处的变量)
* 2、外部函数
* 在类外声明的函数(非成员函数),都具有命名空间作用域,在调用前进行引用性声明就性
* (通常情况下,变量和函数的定义都放在源文件中,对外部变量和外部函数的声明则放在头文件中)
* 3、将函数和变量限制在编译单元内
* 命名空间作用域中声明的变量和函数,在默认情况下都可以被其他编译单元访问
* 对于不希望引用的可以
* 1、使用ststic来进行修饰,ststic修饰的变量和函数将没有办法被别的所引用
* static修饰的都具有静态生存期
* 2、匿名命名空间,
* 可以把不希望引用函数和变量放在匿名命名空间中
* (三)标准c++库
* 是一个非常灵活并且可扩展的可重用软件模块的集合
* 标准的c++在逻辑上分为以下6种类型
* 输入输出类
* 容器类和抽象数据类
* 存储管理类
* 算法
* 错误处理
* 运行环境支持
* using namespase std 不适合放在一个头文件中,防止命名空间对源文件开放
* (四)编译预处理
* 编译器对源程序进行编译之前,要由预处理器对程序文本进行预处理
* 预处理指令不是c++语言的一部分,它是为了扩充c++程序设定的环境
* #引导,每一条预处理指令单独占一行,预处理指令可以根据需要出现在程序的任何位置
* 1、#include指令
* 文件包含指令,将另一个源文件镶嵌带当前源文件,通常用来插入头文件
* 可以嵌套使用
* 2、#define和#undef
* #define:1、可以用来定义宏2、定义空符号,仅仅表示符号已经被定义过了
* #undef:删除定义的宏,使其之后不起作用
* 3、条件编译指令
* 限定条件,在一些条件下,内容才能够被编译
* #if 常量表达式//常量表达式非0,编译
* 程序段
* #else if
*
* #ifdef 标识符//如果标识符被defined定义了且没有被删除,编译
* 程序段
* 4、defined操作符
* defined是一个预处理操作符,而不是一个指令,不需要以#开头
* defined(标识符)
* 如果标识符被#define定义过切没有被#undef删除则表达式为非0,否则为0;
*/
#include<iostream>
#include<math.h>
using namespace std;
//命名空间,全局变量
namespace Q
{
int j;//在命名空间Q中的全局变量
}
int i;//全局变量
//静态数据成员
//静态函数成员
class point
{
public:
point(int x1, int y1) //构造函数
{
x = x1;
y = y1;
count++;
}
static void f(point a);//静态函数成员说明
point(point& p)//拷贝构造函数
{
x = p.x;
y = p.y;
count++;
}
void showpoint();//函数声明
void showcount();//函数声明
~point() { count--; }
private:
int x;
int y;
static int count;//静态成员声明
};
void point::f(point a)
{
//cout << x;//不能直接访问,要用对象去访问
cout << a.x;
}
void point::showpoint()
{
cout << x ;
cout << y << endl;
}
void point::showcount()
{
cout << count << endl;
}
int point::count = 0;//静态数据成员定义和初始化,使用类名限定
//使用友元函数计算两点之间的距离
class point1
{
public:
point1(int a,int b)//构造函数
{
x = a;
y = b;
}
point1(point1& a)//复制函数
{
x=a.x;
y = a.y;
}
friend float jvli(point1 x,point1 y);
~point1() {}//析构函数
private:
int x;
int y;
};
float jvli(point1 p1, point1 p2)//友元函数的实现
{
float x = p1.x - p2.x;//友元函数通过对象访问隐私数据
float y = p1.y - p2.y;
return sqrt(x * x +y * y);
}
//友元类
class A
{
public:
A(int x)
{
i = x;
}
friend class B;
void show()
{
cout << i << endl;
}
private:
int i;
};
class B
{
public:
friend class A;
void set(int j);
private:
A a;
};
void B::set(int j)
{
a.i = j;
}
//常对象
class C
{
public:
C(int x,int y)
{
i = x;
j = y;
}
private:
int i;
int j;
};
const C c(1, 2);//c是常对象不能被更新
//常成员函数
class D
{
public:
D(int x) :a(x) {}
void print();
void print()const;
private:
int a;
};
void D::print()
{
cout << a<<endl;
}
void D::print()const
{
cout << 2 * a << endl;
}
//常数据成员
class E
{
public:
E(int a) :i(a) {}
void print();
private:
const int i;
static const int j;
};
const int E :: j = 2;
void E::print()
{
cout << i <<j<< endl;
}
//匿名命名空间
namespace
{
int a;
}
int main()
{
//常数据成员
E g(1);
g.print();
//常成员函数
D e(1);
e.print();
const D f(1);
f.print();
//友元函数
point1 l1(1, 1);
point1 l2(4, 5);
cout << jvli(l1, l2) << endl;
//静态数据成员
point p(1,2);
p.showpoint();
p.showcount();
point p1(2, 2);
p1.showpoint();
p1.showcount();
//通过p p1访问的是同一个静态成员count,实现两个对象之间的数据共享
///命名空间,全局变量
i = 5;
Q::j = 6;//给命名空间中的j赋值
cout << i << endl;
cout << Q::j << endl;
{
using namespace Q;//直接引用命名空间Q,其所在空间的所有标识符都可以用
i = 6;
cout << i<< endl;
cout << j << endl;
}
}