c++ — 数据的共享与保护
标示符的作用域与可见性
作用域分类:
1、函数原型作用域:
函数原型中的参数,从函数声明的左“(”开始到右“)”结束。
2、局部作用域(块作用域):
函数的形参、在块中声明的标识符。
3、类作用域:
类的成员,范围包括类体和成员函数体。
在类作用域外访问类成员:
静态成员:通过类名,或者该类的对象名、对象引用访问
非静态成员:通过类名,或者该类的对象名、对象引用、对象指针访问。
4、文件作用域:
不在前述各个作用域中出现的声明,就具有文件作用域。始于声明点,结束于文件尾。
5、命名空间作用域(详见后续章节)
可见性
1、可见性是从对标识符的引用的角度来谈的概念;
2、可见性表示从内层作用域向外层作用域“看”时能看见什么;
3、如果标识符在某处可见,就可以在该处引用该标识符。
简单来说:
1、如果某一个标识符在外层声明,在内层没有相同的标识符的声明,则该标识符在内层可见;
2、对于两个嵌套的作用域,如果内层作用域声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见(被覆盖)。
例:
#include <iostream>
using namespace std;
int i;
int main()
{
i=10;
{
int i=5;
cout<<"i="<<i<<endl;
}
cout<<"i="<<i<<endl;
return 0;
}
结果为:
i=5
i=10
对象的生存期
静态生存期:
1、这种生存期与程序的运行期相同;
2、在文件作用域中声明的对象拥有这种生存期;
3、在函数内部声明静态生存期对象要用static关键字(但作用域不改变)。
动态生存期:
1、开始于声明点,结束于该标识符作用域结束处;
2、块作用域中声明的没有static修饰的对象,均为动态生存期。(也称为局部生存期)
#include <iostream>
using namespace std;
int i=1;//i为全局变量,具有静态生存期
void other()
{
static int a=2;
static int b;//a,b为静态局部变量,具有全局寿命,局部可见
//只第一次进入函数时被初始化
int c=10; //c为局部变量,具有动态生存期
//每次进入函数时都被初始化
a+=2;i+=32;c+=5;
cout<<"---Other---\n";
cout<<"i="<<i<<"a="<<a<<"b="<<b<<"c="<<c<<endl;
b=a;
}
int main()
{
static int a; //a为静态局部变量,具有全局寿命,局部可见
int b=-10;
int c=0; //b,c为局部变量,具有动态生存期
cout<<"---Main---\n";
cout<<"i="<<i<<"a="<<a<<"b="<<b<<"c="<<c<<endl;
c+=8;
other();
cout<<"---Main---\n";
cout<<"i="<<i<<"a="<<a<<"b="<<b<<"c="<<c<<endl;
i+=10;
other();
return 0;
}
结果:
类的静态数据成员
1、用关键字static声明;
2、为该类的所有对象共享,静态数据成员具有静态生存期;
3、必须在类外定义和初始化,用“::”来指明所属的类。
#include <iostream>
using namespace std;
class Point{
public:
Point(int x=0,int y=0):x(x),y(y){count++;}
Point(Point &p){
x=p.x;y=p.y;count++;
}
~Point(){count--;}
int getX(){return x;}
int getY(){return y;}
void showCount(){
cout<<"count="<<count<<endl;
}
private:
int x,y;
static int count; //对于同一类的所有对象都是通用的
};
int Point::count=0;
int main()
{
Point a(3,4);
cout<<"A:"<<a.getX()<<","<<a.getY()<<endl;
a.showCount();
Point b(5,6);
cout<<"B:"<<b.getX()<<","<<b.getY()<<endl;
b.showCount();
return 0;
}
类的友元
1、友元时c++中提供的一种破坏数据封装和数据隐藏的机制;
2、通过将一个模块声明为另一个模块的友元,一个模块就能引用另一个模块中本是被隐藏的信息;
3、可以声明友元函数和友元类;
4、为确保数据的完整性及数据封装与隐藏的原则,建议慎用友元。
友元函数:
1、友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private和protected成员;
2、作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择;
3、范文对象中的成员必须通过对象名。
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(int x=0,int y=0):x(x),y(y){}
int getX(){return x;}
int getY(){return y;}
friend double dist(Point &a,Point &b); //dist函数为Point类的友元
private:
int x,y;
};
double dist(Point &a,Point &b)
{
double x=a.x-b.x;
double y=a.y-b.y;
return sqrt(x*x+y*y);
}
int main()
{
Point p1(1,1),p2(4,5);
cout<<"the distance is"<<dist(p1,p2);
return 0;
}
友元类:
1、若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员;
2、语法:将友元类名在另一个类中使用friend修饰说明。
例:
class A{
friend class B; //B为A的友元,B可以访问A的私有成员
public:
void display(){cout<<x<<endl;}
private:
int x;
};
class B{
public:
void set(int i);
void display();
private:
A a;
};
void B::set(int i)
{
a.x=i; //B直接访问A的私有成员x
}
void B::display()
{
a.display;
}
注:
友元关系是单向的,每个类只有授权别的类为友元来访问自己的私有成员,而不能要求自己为别的类的友元去访问别的类的私有成员。
共享数据的保护
常类型:
1、常对象: 必须进行初始化,不能被更新。
const 类名 对象名;
2、常成员:用const进行修饰的类成员。(常数据成员和常函数成员)
3、常引用:被引用的对象不能被更新。(只读的引用)
const 类型说明符 &引用名;
4、常数组:数组元素不能被更新。
类型说明符 const 数组名[大小]… …
5、常指针:指向常量的指针。
常成员函数:
1、使用const关键字说明的函数;
2、常成员函数不更新对象的数据成员;
3、格式:
类型说明符 函数名(参数表) const;
(const是函数类型的一个组成部分,因此在实现部分也要带const关键字)
4、const关键字可以被用于参与对重载函数的区分;
5、通过常对象只能调用它的常成员函数。
常数据成员:
使用const修饰的数据成员。
#include <iostream>
using namespace std;
class A{
public:
A(int i):a(i){} //a仅能在初始化列表中赋值,而不能在构造函数体中赋值
void print();
private:
const int a; //常数据成员,用前必须赋初始值
static const int b; //静态常数据成员,为所有对象所共享
};
const int A::b=10; //将b赋初始值
void A::print()
{
cout<<a<<":"<<b<<endl;
}
int main()
{
A a(100),b(0);
a.print();
b.print();
return 0;
}
结果
常引用:
在友元中用常引用做参数,既能获得较高的执行效率,也能保证实参的安全性。
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(int x=0,int y=0):x(x),y(y){}
int getX(){return x;}
int getY(){return y;}
friend double dist(const Point &a,const Point &b); //常引用
private:
int x,y;
};
double dist(const Point &a,const Point &b)
{
double x=a.x-b.x;
double y=a.y-b.y; //只能读取a.x,a.y,b.x,b.y的值,不能修改
return sqrt(x*x+y*y);
}
int main()
{
Point p1(1,1),p2(4,5);
cout<<"the distance is"<<dist(p1,p2);
return 0;
}
多文件结构和预编译命令
c++程序一般组织结构:
1、一个工程可划分为多个源文件,例如:
类声明文件(. h文件)
类实现文件(. cpp文件)
类的使用文件(main()所在的 . cpp文件)
2、利用工程来组合各个文件
外部变量:
1、除了定义它的源文件中可以使用外,还能被其他文件使用;
2、文件作用域中定义的变量,默认都是外部变量;
3、在其他文件中如果需要使用,需要用extern关键字声明。
外部函数:
1、在所有类之外声明的函数(非成员函数),都是具有文件作用域的;
2、这样的函数可以在不同编译单元中被调用;
3、只要在被调用前进行引用性声明(声明函数原型)即可。
将变量和函数限制在编译单元内:
1、在匿名命名空间中定义的变量和函数,都不会暴露给其他的编译单元;
namespace{ //匿名命名空间
int n;
void f(){}
}
标准c++库:
标准c++库是一个极为灵活并可扩展的可重用软件模块的集合,在逻辑上分为6种类型:
输入输出类
容器类与抽象数据类型
存储管理类
算法
错误处理
运行环境支持
编译预处理命令:
1、#include包含指令
将一个源文件嵌入到当前文件该点处
#include <文件名>
按标准方式搜索。(搜索c++系统默认的安装目录下)
#include “文件名”
先在当前目录搜索,若找不到,则再按标准方式搜索。
2、#define宏定义指令
定义符号常量(很多情况下已被const取代)
定义带参数宏(已被内联函数取代)
3、#undef
删除由#define定义的宏,使其不再起作用。
4、#if、#endif、#else、#elif—条件编译指令
#if 常量表达式 //当常量表达式为真时编译
程序正文
#endif
#if 常量表达式 //当常量表达式为“真”时编译
程序正文1
#else //当常量表达式为“假”时编译
程序正文2
#endif
#if 常量表达式1 //当常量表达式1“非零”时编译
程序正文1
#elif 常量表达式1 //当常量表达式2“非零”时编译
程序正文2
#else //其他情况下编译
程序正文3
#endif
5、#ifdef、#ifndef
#ifdef 标识符
程序段1
#else
程序段2
#endif
如果标识符经define定义过后,未经undef删除,则编译程序段1,否则编译程序段2。
#ifndef 标识符
程序段1
#else
程序段2
#endif
如果标识符未被定义过则编译程序段1,否则编译程序段2。