c++(five)
C++程序的结构
作用域与可见性
-
作用域
作用域讨论的是标识符的有效范围;
-
可见性
可见性是标识符是否可以引用的问题
作用域
作用域是一个标识符在程序正文中有效的区域。
C++的作用域
- 函数原型作用域
- 块作用域(局部作用域)
- 类作用域
- 命名空间作用域
- 文件作用域
函数原形作用域
-
函数原型中的参数,其作用域始于“(”,结束于")"。
-
例如,设有下列原型声明:
double Area(double width, double length); length = 50.0; //错误 // width,length的作用域仅在于此,不能用于程序正文其它地方,因而可有可无
-
等价于以下函数原型声明:
double Area(double , double );
块作用域
-
标识符的声明出现在一对花括号括起来的一段程序块内,其作用域从声明处开始,一直到到块结束处为止。例如:
void fun(int a) { int b(a); cin>>b; //b的作用域 if (b>0) { int c; //c的作用域 ...... } cout << c; //错误 cout << b; //正确 }
类作用域
- 类的作用域是指类定义和相应的成员函数的定义范围。一个类的所有成员位于这个类的作用域内。在该范围内,一个类的成员函数对数据成员具有无限制的访问权
- 在类的作用域外对数据成员的访问是受限制的,这就是类的封装作用
- 把类看成是一组有名成员的集合,除了个别例外情况外,类的作用域作用于特定的成员名
- 类X的一个成员m 在下列情况下局部于类X,或者说具有类作用域
- m出现在类X的成员函数内,该成员函数中没有声明同名的局部作用域的标识符,那么在函数内可以直接访问成员m。
- 在x.M这样的表达式中,其中x为X类的对象。
- 在prt->m这样的表达式中,其中prt为指向X类的一个对象的指针
- 在X::m这样的表达式中用于访问类的静态成员
class Myclass
{
public:
void f1 ( ) { m = 5 ; }
void f2 ( )
{
int m;
m = 2; //Myclass::m被屏蔽
}
int getm()
{
return m;
}
private:
int m;
};
void main()
{
Myclass c;
c.m = 10 ; //错误
c.f1 ( );
cout << c.getm ( ) << endl;
c.f2 ( );
cout << c.getm ( ) << endl;
}
//运行结果
// 5
// 5
命名空间作用域
-
一个大型的程序通常由不同模块构成,不同的模块甚至有可能是由不同人员开发的。不同模块中的类和函数之间有可能发生重名,这样可能引发错误,要解决这类歧义问题就必须使用到命名空间
-
namespace ns1 //指定命名中间nsl { int a; double b; }
-
一个命名空间确定了一个命名空间作用域,凡是在该命名空间之内声明的变量,函数都属于该命名空间作用域。如果要引用,则用命名空间::标识符名
-
namespace ns1 //指定命名空间nsl { int a; double b; } ns1::a
-
还有以下两种都是将标识符暴露在当前的作用域。
-
using 命名空间::标识符名。
-
using 命名空间
-
-
命名空间可以嵌套。
#include <iostream>
using namespace std;
namespace first
{ int x = 5;
int y = 10;
}
namespace second
{ double x = 3.1416;
double y = 2.7183;
}
int main () {
using first::x;
using second::y;
cout << x << endl;
cout << y << endl;
cout << first::y << endl;
cout << second::x << endl;
return 0;
}
#include <iostream>
namespace first
{ int a=10;
int b=20;
namespace second
{
double a=1.02;
double b=5.002;
void hello();
}
void second::hello()
{
std::cout <<"hello world"<<std::endl;
}}
int main()
{ using namespace first;
std::cout<<second::a<<std::endl;
second::hello();
}
全局命名空间和匿名命名空间
注:在匿名命名空间中声明的名称也将被编译器转换,与编译器为这个匿名命名空间生成的唯一内部名称绑定在一起
例:
namespace __UNIQUE_NAME_
{
char c;
int i;
double d;
}
using namespace __UNIQUE_NAME_;
#include <iostream>
using namespace std;
int i; //在全局命名空间中的全局变量
namespace Ns {
int j; //在Ns命名空间中的全局变量}
int main() {
i = 5; //为全局变量i赋值
Ns::j = 6; //为全局变量j赋值
{using namespace Ns; //使得在当前块中可以直接引用Ns命名空间的标识符
int i; //局部变量,局部作用域
i = 7;
cout << "i = " << i << endl;//输出7
cout << "j = " << j << endl;//输出6
}
cout << "i = " << i << endl; //输出5
return 0;}
文件作用域
- 不在前述各个作用域中出现的声明,具有文件作用域(全局作用域),这样声明的标识符的作用域开始于声明点,结束于源文件尾。
- 程序编译的单位是源文件,包含多个函数。全局变量为本文件中所有函数所共用
- 具有文件作用域的变量称为全局变量
例:
int p=1, q=5; /*全局变量*/ //范围:从int p=1, q=5; 到结尾
float f1 (int a)
{ int b, c;
…
}
char c1, c2; /*全局变量*/ //范围:从char c1, c2; 到结尾
char f2 (int x, int y)
{ int i, j;
… }
main()
{ int m, n;
… }
#include <iostream.h>
int i = 2, j=3; //文件作用域
void main( )
{ int i = 5; //块作用域
{ int i; //块作用域
i = 7;
cout<<“i, j=“ <<i << j <<endl;
}
cout<<“i, j = ”<<i << j;
}
void func ( )
{ . . .
cout << “i, j =” << i << j;
}
可见性
-
可见性是从对标识符的引用的角度来谈有效性
-
可见性表示从内层作用域向外层作用域“看”时能看见什么
-
在某一点能够引用到的标识符,就是该处可见的标识符
-
作用域可见性的一般规则
- 标识符应声明在先,引用在后。
- 在同一作用域中,不能声明同名的标识符
- 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见
- 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见,即内层屏蔽
- 在没有互相包含关系的不同的作用域中声明的同名标识符互不影响
int id = 3; //文件作用域 void main( ) { id = 5; { int id; id = 7; cout<<“id=“ <<id << endl; //输出7 } cout<<“id = "<< id << j; //输出5 }
对象的生存期
对象从诞生到结束的这段时间就是它的生存期。在对象生存期内,对象将保持它的状态(State, 即数据成员的值),直到被更新为止
对象的生存期可以分为静态生存期和动态生存期两种。
静态生存期
如果对象的生存期与程序的运行期相同,我们称它具有静态生存期。
-
这种生存期与程序的运行期相同
-
在文件作用域中声明的对象具有这种生存期
-
在函数内部声明静态生存期对象,要冠以关键字static ,称为静态变量
例:
static int i ;
例:
#include <iostream.h>
int i = 5; //文件作用域
int main( )
{
cout<<"i="<< i <<endl;
return 0;
}
//i 具有静态生存期
动态生存期
- 块作用域中声明的对象是动态生存期的对象(习惯称局部生存期对象)
- 动态生存期程序诞生于声明点,结束于该标识符的作用域结束处
void fun( );
void main( )
{ fun( );
fun( );
}
void fun( )
{ static int a = 1;
int i=5;
a++;
i++;
cout<<"i="<<i<<",a="<<a<<endl;
}
//运行结果:
//i=6, a=2
//i=6, a=3
//i是动态生存期
//a是静态生存期
例:具有静态、动态生存期对象的时钟程序
#include<iostream.h>
class Clock //时钟类声明
{
public: //外部接口
Clock( );
void SetTime(int NewH, int NewM, int NewS);
//三个形参均具有函数原型作用域
void ShowTime( );
~ Clock( ){}
private: //私有数据成员
int Hour,Minute,Second;
};
//时钟类成员函数实现
Clock::Clock( )//构造函数
{ Hour=0;
Minute=0;
Second=0;
}
void Clock::SetTime(int NewH, int NewM, int NewS)
{ Hour=NewH;
Minute=NewM;
Second=NewS;
}
void Clock::ShowTime( )
{ cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}
Clock globClock;
//声明对象globClock,具有静态生存期,
//文件作用域
void main( ) //主函数
{ cout<<"First time output:"<<endl;
//引用具有文件作用域的对象globClock:
globClock.ShowTime( ); //对象的成员函数//具有类作用域
globClock.SetTime(8,30,30);
Clock myClock (globClock); //声明具有块作 //用域的对象myClock
cout<<"Second time output:"<<endl;
myClock.ShowTime( ); //引用具有块作用//域的对象myClock
}
//程序的运行结果为:
//First time output:
//0:0:0
//Second time output:
//8:30:30
数据与函数
**“数据结构+算法=程序设计”,它是结构化程序设计的基础。数据的组织就是数据结构,函数是来实现算法的。 **
- 数据存储在局部对象中,通过参数传递实现共享——函数间的参数传递。
- 数据存储在全局对象中。可以被整个程序中所有函数共享
- 在面向对象程序设计中,数据描述的是对象的属性、状态
- 函数是算法的实现,是用来处理数据、改变对象状态的
- 将数据和使用数据的函数封装在类中。类的数据成员被类的成员函数共享
#include<iostream.h>
int global;
void f( )
{ global=5;}
void g( )
{ cout<<global<<endl;}
int main( )
{ f( );
g( ); //输出“5”
return 0;
}
#include<iostream.h>
class Application
{ public:
void f( ); void g( );
private:
int global;
};
void Application::f( )
{ global=5;}
void Application::g( )
{ cout<<global<<endl;}
int main( )
{
Application MyApp;
MyApp.f( );
MyApp.g( );
return 0;
}
局部变量
- 局部变量:就是具有块作用域的变量
- 如果把数据存储在局部变量中,函数在不同的块之间只能通过参数传递来共享数据
全局变量
- 全局变量:具有文件作用域
- 在整个程序中,除了在定义有同名局部变量的块中之外,都有可以进行直接访问。将数据存放在全局变量中,不同的函数在不同的地方对同一全局变量进行访问,就实现了函数之间的数据共享
#include<iostream.h>
int global;
void f( )
{ global=5;}
void g( )
{ cout<<global<<endl;}
int main( )
{ f( );
g( ); //输出“5”
return 0;
}
数据成员
- 类的成员
- 数据成员
- 函数成员
- 类中的数据成员可以被同一类中任何一个函 数访问
- 在类内部的函数之间实现了数据的共享,另一方面这种共享是受限制的,可以设置适当的访问控制属性,把共享只限制在类的范围之内,这样对类外来说,类的数据成员仍然是隐藏的,达到了共享与隐藏两全
#include<iostream.h>
class Application
{ public:
void f( ); void g( );
private:
int global;
};
void Application::f( )
{ global=5;
}
void Application::g( )
{ cout<<global<<endl;
}
int main( )
{
Application MyApp;
MyApp.f( );
MyApp.g( );
return 0;
}
#include<iostream.h>
class Clock //时钟类声明
{
public: //外部接口
void SetTime(int NewH, int NewM, int NewS);
//三个形参均具有函数原型作用域
void ShowTime( );
private: //私有数据成员
int Hour,Minute,Second;
}; //在类的成员函数之间共享
类的静态成员
-
静态成员的引用
例:抽象出某公司全体雇员的共性,设计如下雇员类:
class employee { private: int EmpNo; int ID; char *name; //以字符指针指向字符串首地址 … // 其他数据成员与函数成员略 }
静态数据成员
-
实例属性
-
类属性
描述类的所有对象的共同特性的一个数据项,对于任何对象实例,它的属性值是相同的。在C++语言中是通过静态数据成员来实现“类属性”的
-
用关键字static声明:在类的定义中仅仅对静态数据成员进行引用性声明,必须在文件作用域的某个地方使用类名限定定义性声明,这时也可以进行初始化。
-
该类的所有对象维护该成员的同一个拷贝
-
必须在类外定义和可以进行初始化,用(::)来指明所属的类
-
一般通过类名进行访问, 类名::标识符。也可以通过对象名.标识符访问。(这两种方式都必须是公开权限)
include <iostream>
using namespace std;
class A
{
public:
static int x;
};
int A::x;
void main()
{
cout<<A::x<<endl;
A a;
cout<<a.x<<endl;
}
例5-4 具有静态数据成员的 Point类
#include < iostream.h>
class Point
{ public:
Point(int xx=0, int yy=0)
{ X = xx; Y = yy; countP++; }
Point(Point &p);
int GetX( ) {return X;}
int GetY( ) {return Y;}
void GetC( ) {
cout<<" Object
id="<<countP<<endl;
}
private:
int X,Y;
static int countP;
};
Point::Point(Point &p)
{ X=p.X;
Y=p.Y;
countP++;
}
int Point::countP=0; /*静态数据成员定义性声明和初始化,使用类名限定*/
void main( )
{
Point :: GetC ( ); //错误
Point A(4,5);
cout<<"Point A,"<<A.GetX( )
<<","<<A.GetY( );
A.GetC( );
Point B(A);
cout<<"Point B,"<<B.GetX( )<<","<<B.GetY( );
B.GetC( );
}
静态成员函数
-
可以使用类名或对象名来调用公有的静态成员函数。
-
对普通成员函数只能通过对象名来调用
-
静态成员函数
使用static关键字声明的函数成员
-
静态成员函数可以直接访问该类的静态(数据和函数)成员;而访问非静态(数据和函数)成员,要访问非静态成员必须通过参数传递方式得到对象名,然后通过对象名来访问
静态成员函数举例
#include<iostream.h>
class Application
{ public:
static void f( );
static void g( );
private:
static int global;
};
int Application::global=0;
void Application::f( )
{ global=5;}
void Application::g( )
{ cout<<global<<endl;}
void main( )
{
Application::f( );
Application::g( );
}
- 静态成员属于类,非静态成员属于对象
- 静态成员函数只能引用属于该类的静态数据成员或静态成员函数。
- 由于静态成员不是对象成员,所以在静态成员函数 f( ) 的实现中不能直接引用类中声明的非静态成员
- 在 f( ) 的实现中,可以通过对象 a 来访问 x —— a.x
class A
{ public:
static void f(A a);
private:
int x;
};
void A::f(A a)
{
cout<<x; //对x的引用是错误的
cout<<a.x; //正确
}
例:具有静态数据、函数成员的 Point类
class Point //Point类声明
{
public: //外部接口
Point(int xx=0, int yy=0)
{ X=xx; Y=yy; countP++;}
Point(Point &p); //拷贝构造函数
int GetX( ) {return X;}
int GetY( ) {return Y;}
static void GetC( )
{cout<<" Object id="<<countP<<endl;}
private: //私有数据成员
int X,Y;
static int countP;
}
Point::Point(Point &p)
{ X=p.X;
Y=p.Y;
countP++;
}
int Point::countP=0;
void main( ) //主函数实现
{ Point A(4,5); //声明对象A
cout<<"Point A,"<<A.GetX( )
<<","<<A.GetY( );
A.GetC( ); //输出对象号,对象名引用
Point B(A); //声明对象B
cout<<"Point B,"<<B.GetX( )
<<","<<B.GetY( );
Point::GetC( ); //输出对象号,类名引用
}
友元
- 友元是C++提供的一种破坏数据封装和数据隐藏的机制
- 通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息
- 可以使用友元函数和友元类。
- 为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。
友元函数
- 友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private 和 protected 成员。
- 可以是一个普通函数,或其他类的成员函数,不是本类的成员函数。
- 作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
- 访问对象中的成员必须通过对象名。
例:使用友元函数计算两点距离
#include <iostream.h>
#include <math.h>
class Point //Point类声明
{ public: //外部接口
Point(float xx=0, float yy=0) {X=xx;Y=yy;}
int GetX( ) {return X;}
int GetY( ) {return Y;} // 友元函数声明
friend float fDist(Point &a, Point &b);
private: //私有数据成员
int X,Y; };
float fDist( Point& a, Point& b)
{ // 友元函数定义
double dx=a.X-b.X;
double dy=a.Y-b.Y;
return sqrt(dx*dx+dy*dy);
}
void main( )
{ Point p1(3.0, 5.0), p2(4.0, 6.0);
float d=fDist (p1, p2);
cout<<"The distance is "<<d<<endl;
}
友元类
- 若A类为B类的友元类,则A类的所有成员函数都能访问B类的私有成员和保护成员。
- 声明语法:将友元类名在另一个类中使用friend修饰说明。
一般的语法形式:
class B
{
…… //B类的成员声明
friend class A ; //声明A为B 的友元类
……
}
通过友元类声明,友元类的成员函数可以通过对象名直接访问到隐藏的数据,达到高效协调工作的目的
友元类举例:
class A
{
public:
void Display( ) {cout<<x<<endl;}
friend class B;
private:
int x;
}
class B
{
public:
void Set(int i);
void Display( );
private:
A a;
};
void B::Set(int i)
{
a.x = i;
}
void B::Display( )
{
a.Display( );
}
共享数据的保护
-
常量及格式:
-
const <类型说明符> <变量名> = <常量或常量表达式>;
-
<类型说明符> const <变量名> = <常量或常量表达式>;
当一个变量被const修饰后,具有以下几个特点:
-
该变量只能读取不能修改。(编译器进行检查)
-
定义时必须初始化。
-
C++中喜欢用const来定义常量,取代原来C风格的预编译指令define。
const int var; // Error:常量 变量"var"需要初始化设定项 const int var1 = 42; var1 = 43; // Error:表达式必须是可以修改的左值
-
-
-
常量
注意,在使用const变量作为数组的下标时,变量的值一定要是一个常量表达式(在编译阶段就能计算得到结果)。
const int sz = 42; int iAr[sz]; const int sz1 = size(); // size()必须是一个返回常量的函数 int iAr1[sz1]; int var = 42; const int sz2 = var; int iAr2[sz2]; // error:sz2只有运行时才知道值
常引用
声明或定义的格式如下:
const <类型说明符> &<变量名> = …… [1]
<类型说明符> const &<变量名> = …… [2]
const引用就是指向const对象或常变量的引用。
普通引用不能绑定到const对象或常变量,但const引用可以绑定到非const 对象或常变量。
const int ii = 456;
int &rii = ii; // error
int jj = 123;
const int &rjj = jj; // ok
非const引用只能绑定到与该引用同类型的对象或变量。
const 用则可以绑定到不同但相关的类型的对象或绑定到右值。
例如:
const int &r = 100;//绑定到字面值常量
int i = 50;
const int &r2 = r + i; // 引用 r 绑定到右值
double dVal = 3.1415;
const int &ri = dVal; // 整型引用绑定到 double 类型编译器会把以上代码转换成如下形式的编码:
int temp = dVal; // create temporary int from double
const int &ri = temp; // bind ri to that temporary
常对象
数据成员的值在整个生存期内不能被改变。
常对象必须进行初始化,不能被更新。
定义常对象的一般形式为:
类名 const 对象名(实参列表);
const 类名 对象名(实参列表);
Time const t1(12,34,36); //定义t1为常对象
在所有的场合中,对象t1中的所有数据成员的值都不能被修改。凡希望保证数据成员不被改变的对象,可以声明为常对象。
如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数(除了由系统自动调用的隐式构造函数和析构函数)。
例: 常引用做形参
void display ( const double& r );
void main( )
{ double d ( 9.5 );
display(d);
}
void display(const double& r)
/*常引用做形参,在函数中不能更新 r所
引用的对象。 */
{ r = 15.5; //错误
cout<<r<<endl; }
常对象举例
class A
{
public:
A( int i, int j ) { x=i; y=j; }
...
private:
int x, y;
};
A const a(3,4); /*a是常对象,只能初始化, 不能被更新*/
在C++的语法中,对基本数据类型的常量提供了可靠的保护。
int const n = 10 ;
n = 20 ; //错误
用const修饰的对象成员
-
用const修饰的声明数据成员称为常数据成员
有两种声明形式:
const int cctwl; int const cctwl;
不能省略数据类型,可以添加 public private等访问控制符。 说明:
- 任何函数都不能对常数据成员赋值。(除构造函数)
- 构造函数对常数据成员进行初始化时也只能通过初始化列表进行。
- 常数据成员在初始化时必须赋值或称其必须初始化.
- 如果类有多个构造函数必须都初始化常数据成员。
-
#include <iostream> using namespace std; class A { int w,h; const int cctw=5;//错误一 }; void main() { A a; //错误二 cout<<"sss"; }
错误一:不能对常数据成员在类中初始化、要通过类的构造函数。
错误二:没有合适的默认构造函数可用。因为有常量cctwl没有初始化必须初始化所有常数据成员。
-
#include <iostream> using namespace std; class A { int w,h; const int cctw; public: const int cctwl; A():cctw(5),cctwl(8){}; };
-
多个构造函数下的常数据成员
#include <iostream> using namespace std; class A { int w,h; const int cctw; public: const int cctwl; A():cctw(5),cctwl(8){}; A(int x,int y){w=x,h=y;}};//错误一
错误一:每个构造函数都要初始化常数据成员,应改为:
A()(int x,int y): cctw(5),cctwl(8) {w=x,h=y;}};
-
常成员函数
-
常成员函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。
-
常成员函数说明格式
类型说明符 函数名(参数表)const;
-
const是函数类型的一个组成部分,因此在实现部分也要带const关键字。
-
const关键字可以被用于参与对重载函数的区分,例:
void print( ); void print( ) const;
-
-
通过常对象只能调用它的常成员函数
-
常数据成员
- 使用const说明的数据成员。
- 构造函数对常数据成员进行初始化,只能通过初始化列表。
-
用const修饰的声明成员函数称为常成员函数:
- const是函数类型的一部分,在实现部分也要带该关键字
- const关键字可以用于对重载函数的区分
- 常成员函数不能更新任何数据成员,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。
-
const是函数类型的一部分,在实现部分也要带该关键字:
#include <iostream> using namespace std; class A { int w,h; public: int getValue() const; int getValue(); A(int x,int y) {w=x,h=y; } }; int A::getValue() const {return w*h;} int A::getValue() {return h;} void main() { A const a(3,4); A c(2,6); cout<<a.getValue()<<" "<<c.getValue(); }
常成员函数举例:
#include<iostream.h>
class R
{ public:
R(int r1, int r2) { R1=r1; R2=r2;}
void print( );
void print( ) const;
private:
int R1, R2;
};
void R::print( )
{ cout<<R1<<":"<<R2<<endl; }
void R::print( ) const
{ cout<<R1<<";"<<R2<<endl; }
void main( )
{ R a(5,4);
a.print( ); //调用void print( )
const R b(20, 52); //b是常对象
b.print( ); //调用void print( ) const
}
//运行结果
//5:4
//20:52
#include <iostream>
using namespace std;
class A
{ public:
A(int i);
void print();
const int& r;
private:
const int a;
static const int b; //静态常数据成员
const int A::b=10; //静态常数据成员在类外声明和初始化
A::A(int i):a(i),r(a)
{}
void A::print()
{
cout<<a<<":"<<b<<":"<<r<<endl;
}
void main()
{/*建立对象a1和a2,并以100和0作为初值,分别调用构造函数,通过构造函数的初始化列表给对象的常数据成员赋初值*/
A a1(100), a2(0);
a1.print();
a2.print();
}
//运行结果:
//100:10:100
//0:10:0
注意 :
- 常成员函数可以被其他成员函数调用
- 但是不能调用其他非常成员函数
- 可以调用其他常成员函数
多文件结构编译预处理命令
C++程序的一般组织结构(多文件结构)
- 一个源程序一般至少分为三个源文件:
- 类声明文件(.h文件)
- 类实现文件(.cpp文件)
- 类的使用文件(main( )所在的.cpp文件)
- 利用工程来组合各个文件。
例:具有静态数据、函数成员的Point类,多文件组织
//文件1,类的声明,point.h
# include < iostream.h>
class Point //Point类声明
{ public: //外部接口
Point(int xx=0, int yy=0)
{ X=xx; Y=yy; countP++;}
Point(Point &p); //拷贝构造函数
int GetX( ) {return X;}
int GetY( ) {return Y;}
static void GetC( )
{cout<<" Object id="<<countP<<endl;}
private: //私有数据成员
int X,Y;
static int countP; //静态数据成员
};
//文件2,类实现,point.cpp
# include “point.h”
Point::Point(Point &p)
{ X=p.X;
Y=p.Y;
countP++;
}
//文件3,主函数,fmain.cpp
# include “point.h”
int Point::countP=0;
void main( ) //主函数实现
{ Point A(4,5); //声明对象A
cout<<"Point A,"<<A.GetX( )
<<","<<A.GetY( );
A.GetC( ); //输出对象号,对象名引用
Point B(A); //声明对象B
cout<<"Point B,"<<B.GetX( )
<<","<<B.GetY( );
Point::GetC( ); //输出对象号,类名引用
分析:
编译预处理命令
-
#include 指令(文件包含指令)
-
将另一个源文件嵌入到当前源文件中该点处
-
#include <文件名>
- 按标准方式搜索,文件位于C++系统目录的include子目录下
-
#include “文件名”
- 首先在当前目录中搜索,若没有,再按标准方式搜索。
-
#include指令可以嵌套使用
有一头文件myhead.h,在中又可嵌套文件包含指令:
#include “file1.h” #include “file2.h”
-
-
#define宏定义指令
-
定义符号常量,已被const定义语句取代。
例:
#define PI 3.14159
-
定义带参数宏,已被内联函数取代。
-
-
#undef
- 删除由#define定义的宏,使之不再起作用
-
#define定义空符号
# define LETTER
-
条件编译指令
-
#if 和 #endif
#if 常量表达式 //当“ 常量表达式”非零时编译本程序段 程序正文 #endif
-
条件编译指令—— #else
#if 常量表达式 //当“ 常量表达式”非零时编译 程序正文1 #else //当“ 常量表达式”为零时编译 程序正文2 #endif
# define LETTER 1 main( ) { char str[20] =“Clanguage”,c; int i; i = 0; While((c = str[i])!=‘\0’) { i++; # if LETTER if (c>=‘a’&&c<=‘z’) c = c-32; # else if (c>=‘A’&&c<=‘Z’) c = c+32; # endif
-
条件编译指令 #elif
#if 常量表达式1 程序正文1 //当“ 常量表达式1”非零时编译 #elif 常量表达式2 程序正文2 //当“ 常量表达式2”非零时编译 #else 程序正文3 //其它情况下编译 #endif
-
条件编译指令 #ifdef
#ifdef 标识符 程序正文1 #else 程序正文2 #endif #ifdef 标识符 程序正文1 #endif
如果“标识符”经**#defined**定义过,且未经undef删除,则编译程序正文1,否则编译程序正文2。如果没有则编译程序正文2,如果没有程序正文2,则#else可以省略:
例:
# ifdef COMPUTER_A # define INTEGER_SIZE 16 # else # define INTEGER_SIZE 32 # endif
即如果COMPUTER_A在前面已被定义过,则编译命令行:
# define INTEGER_SIZE 16 //否则,编译 # define INTEGER_SIZE 32
-
条件编译指令 #ifndef
#ifndef 标识符 程序正文1 #else 程序正文2 #endif
如果“标识符”未被定义过,则编译程序正文1,否则编译程序正文2。如果没有程序正文2,则#else可以省略:
#ifdef 标识符 程序正文1 #endif
-
-
defined(标识符)
define是一个预处理操作符,而不是指令,因此不要以#开头。
**格式: **
define(标识符)
功能:
若“标识符”在此前经**#defined**定义过,且未经undef删除,则上述表达式为非0,否则上述表达式的值为0。
例:下面两种写法完全等效
#ifndef MYHEAD_H #define MYHEAD_H ... #endif #if ! Define(MYHEAD_H) #define MYHEAD_H ... #endif
注意:由于文件包含指令可以嵌套使用,在设计程序时要避免多次重复包含同一个头文件,否则会引起变量及类的重复定义
// main.cpp #include “file1.h” #include “file2.h” void main( ) { …… } //file1.h #include “head.h” …… //file2.h #include “head.h” …… //head.h …… class Point { …… } …… //head.h #ifndef HEAD_H #define HEAD_H …… class Point { …… } …… #endif