导图社区 cpp专题知识框架--类的设计
c 的知识点框架思维导图,类的设计相关,有详细知识点框架注解与丰富示例,快来看看吧!
编辑于2023-05-31 01:50:43 江苏省类的设计
自引用指针
定义
1.每一个类的成员函数都有一个隐藏定义的常量指针,我们把它称为this指针。
2.this指针的类型就是成员函数所属的类的类型
3. 每当调用成员函数时,它被初始化为被调函数所在类的对象的地址
4. 在通常情况下,this指针在系统中是隐含地存在的。也可以将其显式地表示出来。
例子
cout<<“x=”<<this->x<<endl; Box类的volume计算体积函数为 float Box::volume() { return height * width * length; } 等价于 float Box::volume() { return this->height * this->width * this->length; }
例子2
#include<iostream.h> class Sample{ private: int x,y; public: Sample(int i=0,int j=0) { x=i; y=j; } void copy(Sample& xy); void print() { cout<<x<<","<<y<<endl; } }; void Sample::copy(Sample& xy) { if (this==&xy) return; *this=xy; } void main() { Sample p1,p2(5,6); p1.copy(p2); p1.print(); } 运行结果: 5,6
特点
隐式存在
this指针是隐式使用的,它是作为参数被传递给成员函数的。
自动实现
是编译系统自动实现的,程序设计者不必人为地在形参中增加this指针,也不必将对象a的地址传给this指针。
*this
表示对象本身,(*this).height 与 this->height等价
注意事项
同一类的不同对象在调用同一个成员函数时,所创建的this指针不是同一个,因为本质上是被调函数所在类的对象的地址
this指针是一个const指针,不能在程序中修改它或给它赋值。
this指针是一个局部数据,它的作用域仅在一个对象的内部。
对象数组与对象指针
对象数组
定义
对象数组是指每一数组元素都是对象的数组
格式
类名 数组名[下标表达式];
exam ob[4]; //定义了类exam的对象数组ob.
访问
与基本数据类型的数组一样,在使用对象数组时也只能访问单个数组元素,也就是一个对象,通过这个对象,也可以访问到它的公有成员
一般形式
数组名[下标].成员名
初始化
定义对象数组时,可通过初始化表进行赋值。
main() { exam ob1[4]={11,22,33,44}; exam ob2[4]={55,66}; exam ob3[4]={exam(11),exam(22),exam(33),exam(44)}; exam ob4[4]={exam(55),exam(66)}; ob4[2]=exam(77); ob4[3]=exam(88); int i; for (i=0;i<4;i++) cout<<ob1[i].get_x()<<' '; cout<<endl; for (i=0;i<4;i++) cout<<ob2[i].get_x()<<' '; cout<<endl; for (i=0;i<4;i++) cout<<ob3[i].get_x()<<' '; cout<<endl; for (i=0;i<4;i++) cout<<ob4[i].get_x()<<' '; cout<<endl; return 0; } 结果 11 22 33 44 55 66 123 123 11 22 33 44 55 66 77 88
注意事项
如果需要建立某个类的对象数组,在设计类的构造函数时就要充分考虑到数组元素初始化的需要
当各个元素的初值要求为相同的值时,应该在类中定义出不带参数的构造函数或带缺省参数值的构造函数;
当各元素对象的初值要求为不同的值时需要定义带形参(无缺省值)的构造函数。
对象指针
定义
对象指针就是用于存放对象地址的变量
格式
类名* 对象指针名
定义指针变量:Date *p,date1;
初始化
指向一个已创建的对象 p=&date1;
访问
用“->”操作符,只能访问该对象的公有成员。
例子
#include<iostream.h> class exe{ public: void set_a(int a){ x=a; } void show_a(){ cout<<x<<endl; } private: int x; }; main() { exe ob,*p; // 声明类exe的对象ob和类exe的对象指针p ob.set_a(2); ob.show_a(); // 利用对象名访问对象的成员 p=&ob; // 将对象ob的地址赋给对象指针p p->show_a(); // 利用对象指针访问对象的成员 return 0; } 结果 2 2
c++中,必须创建类作用域下的变量指针,来存放类中数据成员的地址嘛? 不必。不论是普通指针还是类作用域下的变量指针,都可以来存放类中数据成员的地址。但是,类作用域下的变量指针可以更方便地访问类中数据成员,因此建议使用这种方式来存放类中数据成员的地址。
用对象指针访问对象数组
对象指针不仅能访问单个对象,也能访问对象数组. 例如: exe *p; //声明对象指针p exe ob[2]; //声明对象数组ob[2] p=ob; //将对象数组的首地址赋给对象指针 例如将例4.7的main()改写为: main() { exe ob[2],*p; ob[0].set_a(10); ob[1].set_a(20); p=ob; p->show_a(); p++; p->show_a(); return 0; } 结果:10 20
指向对象成员的指针
定义
可以为类的对象中的具体成员,如变量、函数或者对象创建指针,存放这些元素的地址,进而可以通过指针解引用来访问这些对象。一样只能访问公有权限的成员。
指向数据成员的指针
声明
类型说明符 类名:: *数据成员指针名;
初始化
数据成员指针名=&类名:: 数据成员名
访问
对象名. *数据成员指针名 对象指针名-> *数据成员指针名
例子
#include<iostream.h> class A{ public: A(int i) { z=i; } int z; }; void main() { A ob(5); A *pc1; // 声明一个对象指针pc1 pc1=&ob; // 给对象指针pc1赋值 int A∷*pc2; // 声明一个数据成员指针pc2 // ① pc2=&A∷z; // 给数据成员指针pc2赋值 // ② cout<<ob.*pc2<<endl; // 用成员指针pc2访问数据成员z cout<<pc1->*pc2<<endl; // 用成员指针pc2访问数据成员z cout<<ob.z<<endl; // 使用对象名访问数据成员z }
指向成员函数的指针
声明
类型说明符 (类名∷ *指针名)(参数表);
初始化
成员函数指针名 = 类名∷成员函数名;
访问
(对象名.*成员函数指针名)(参数表) ; (对象指针名 -> *成员函数指针名)(参数表) ;
例子
#include<iostream.h> class Coord { public: Coord(int a=0,int b=0) { x=a; y=b; } int getx() { return x; } int gety() { return y; } private: int x,y; }; void main() { Coord op(5,6); Coord *pc1=&op; int (Coord∷*pc_getx)(); pc_getx=Coord∷getx; cout<<pc1->getx()<<endl; cout<<op.getx()<<endl; cout<<(op.*pc_getx)()<<endl; cout<<(pc1->*pc_getx)()<<endl; }
向函数传递对象
定义
对象可以作为参数传递给函数,其方法与传递其他类型的数据相同。在向函数传递对象时,是通过传值调用传递给函数的。因此,函数中对对象的任何修改均不影响调用该函数的对象本身。
注意:所谓的值传递,实际上是调用了拷贝构造
例子
#include<iostream.h> class aClass{ public: aClass(int n) { i=n; } void set(int n){ i=n; } int get( ){ return i; } private: int i; }; void sqr(aClass ob) { ob.set(ob.get()*ob.get()); cout<<"copy of obj has i value of "; cout<<ob.get()<<"\n"; } main() { aClass obj(10); sqr(obj); cout<<"But, obj.i is unchanged in main:"; cout<<obj.get( ); return 0; } 结果 copy of obj bas i value of 100 But,obj.i is unchanged in main∶10
使用对象指针作为函数参数
对象指针可以作为函数的参数
通过传递对象的地址,形参以指针的形式进入函数体,只会拷贝一份地址,并不会创建一个新的对象副本,因此可以提高运行效率。
当函数的形参是对象指针时,调用函数的对应实参应该是某个对象的地址值。
例子
#include<iostream.h> class aClass { public: aClass(int n) { i=n; } void set(int n){ i=n; } int get(){ return i;} private: int i; }; void sqr(aClass *ob) { ob->set(ob->get() * ob->get()); cout<<"Copy of obj has i value of "; cout<<ob->get()<<"\n"; } main() { aClass obj(10); sqr(&obj); cout<<"Now, obj.i in main() has been changed :"; cout<<obj.get() <<"\n"; return 0; }
使用对象引用作为函数参数
使用对象引用作为函数参数不但具有用对象指针作函数参数的优点,而且用对象引用作函数参数将更简单、更直接。
形参为引用时,进入函数后,不会发生任何拷贝,因而更为精简
例子
#include<iostream.h> class aClass { public: aClass(int n) { i=n; } void set(int n) { i=n; } int get() { return i;} private: int i; }; void sqr(aClass& ob) { ob.set(ob.get() * ob.get()); cout<<"Copy of obj has i value of "; cout<<ob.get()<<"\n"; } main() { aClass obj(10); sqr(obj); cout<<"Now, obj.i in main() has been changed :"; cout<<obj.get() <<"\n"; return 0; }
静态成员
目的
实现一个类的不同对象之间数据和函数共享
有点像全局变量,只不过作用范围是类
支持静态数据成员的一个主要原因是可以不必使用全局变量。静态数据成员的主要用途是定义类的各个对象所公用的数据。
静态数据成员
特点
用关键字static声明
该类的所有对象维护该成员的同一个拷贝
必须在类外定义和初始化,用(::)来指明所属的类。
私有静态数据成员不能被类外部函数访问,也不能用对象进行访问。
声明
在一个类中,将一个数据成员的原先声明前加上static
初始化格式
<数据类型><类名>∷<静态数据成员名>=<值>;
初始化位置:定义对象之前,一般在类定义后,main()前进行。
说明
静态数据成员的初始化需要在类的外部进行,通过类名对它进行访问。
初始化时不加该成员的访问权限控制符private,public等。
初始化时使用作用域运算符来标明它所属的类,因此,静态数据成员是类的成员,而不是对象的成员。
访问
可用类名访问: 类名::静态数据成员 也可用对象访问: 对象名.静态数据成员 对象指针->静态数据成员
如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。
例子
#include<iostream.h> #include<string.h> class Student { public: Student(char *name1,char *stu_no1,float score1); ~Student(); void show(); // 输出姓名、学号和成绩 void show_count_sum_ave(); // 输出学生人数 private: char *name; // 学生姓名 char *stu_no; // 学生学号 float score; // 学生成绩 static int count; // 静态数据成员,统计学生人数 static float sum; // 静态数据成员,统计总成绩 static float ave; // 静态数据成员,统计平均成绩 }; Student::Student(char *name1,char *stu_no1,float score1 ) { name=new char[strlen(name1)+1]; strcpy(name,name1); stu_no=new char[strlen(stu_no1)+1]; strcpy(stu_no,stu_no1); score=score1; ++count; // 累加学生人数 sum=sum+score; // 累加总成绩 ave=sum/count; // 计算平均成绩 } Student::~Student() { delete []name; delete []stu_no; --count; sum=sum-score; } void Student::show() { cout<<"\n name: "<<name; cout<<"\n stu_no: "<<stu_no; cout<<"\n score: "<<score; } void Student::show_count_sum_ave() { cout<<"\n count: "<<count; // 输出静态数据成员count cout<<"\n sum: "<<sum; // 输出静态数据成员sum cout<<"\n ave: "<<ave; // 输出静态数据成员ave } int Student::count=0; // 静态数员count初始化 float Student::sum=0.0; // 静态数员sum初始化 float Student::ave=0.0; // 静态数员ave初始化 void main() { Student stu1("Liming","990201",90); stu1.show(); stu1.show_count_sum_ave(); Student stu2("Zhanghao","990202",85); stu2.show(); stu2.show_count_sum_ave(); }
静态成员函数
特点
类外代码可以使用类名和作用域操作符来调用公有静态成员函数。
所谓静态成员函数就是使用static关键字声明函数成员。同静态数据成员一样,静态成员函数也属整个类,由同一个类的所有对象共同维护,为这些对象所共享。
静态成员函数只能引用属于该类的静态数据成员或静态成员函数。访问非静态数据成员,必须通过参数传递方式得到对象名,通过对象名访问。
比较
(公有)静态成员函数
可以通过类名和对象名来访问
(公有)非静态成员函数
只能通过对象名调用
声明
static 返回类型 静态成员函数名(参数表);
访问
类名::静态成员函数名(实参表) 对象. 静态成员函数名(实参表) 对象指针->静态成员函数名(实参表)
说明
静态成员函数可以定义成内嵌的,也可以在类外定义,在类外定义时不能用static前缀。
静态成员函数主要用来访问全局变量或同一个类中的静态数据成员。特别是,当它与静态数据成员一起使用时,达到了对同一个类中对象之间共享数据进行维护的目的。
私有静态成员函数不能被类外部函数和对象访问。
使用静态成员函数的一个原因是,可以用它在建立任何对象之前处理静态数据成员。这是普通成员函数不能实现的。
静态成员函数中没有指针this,所以静态成员函数不访问类中的非静态数据成员,若确实需要则只能通过对象名(作为参数)访问。
例子
#include<iostream.h> #include<string.h> class Student { private: char *name; char *stu_no; float score; static int count; static float sum; public: Student(char *name1,char *stu_no1,float score1); ~Student(); void show(); static void show_count_sum(); }; Student::Student(char *name1,char *stu_no1,float score1 ) { name=new char[strlen(name1)+1]; strcpy(name,name1); stu_no=new char[strlen(stu_no1)+1]; strcpy(stu_no,stu_no1); score=score1; ++count; // 累加学生人数 sum=sum+score; // 累加总成绩 } Student::~Student() { delete []name; delete []stu_no; --count; sum=sum-score; } void Student::show() { cout<<"\n name: "<<name; cout<<"\n stu_no: "<<stu_no; cout<<"\n score: "<<score; } void Student::show_count_sum() // 静态成员函数 { cout<<"\n count: "<<count; // 输出静态数据成员 cout<<"\n sum: "<<sum; // 输出静态数据成员 } int Student::count=0; // 静态数据成员初始化 float Student::sum=0.0; // 静态数据成员初始化 void main() { Student stu1("Liming","990201",90); stu1.show(); Student::show_count_sum(); // 使用类名访问静态成员函数 Student stu2("Zhanghao","990202",85); stu2.show(); stu2.show_count_sum(); // 使用对象stu2访问静态成员函数 }
#include<iostream.h> class small_cat { public: small_cat(double w) { weight=w; total_weight+=w; total_number++; } static void display(small_cat& w)//访问非静态成员 { cout<<"The small_cat weights "<<w.weight<<" kg\n";} static void total_disp() //访问静态成员 { cout<<total_number<<" small_cat total_weight "; cout<<total_weight<<"kg "<<endl; } private: double weight; static double total_weight; static double total_number; }; double small_cat∷total_weight=0; double small_cat∷total_number=0; int main() { small_cat w1(0.9),w2(0.8),w3(0.7); small_cat∷display(w1); small_cat∷display(w2); small_cat∷display(w3); small_cat∷total_disp(); return 0; }
静态成员函数访问非静态数据成员
通过普通指针,来存放静态成员的地址
通过指针访问类的静态数据成员。 #include<iostream.h> class myclass { public: myclass() // 构造函数,每定义一个对象, { ++i; } 静态数据成员i加1 static int i; // 声明静态数据成员i }; int myclass::i=0; // 静态数据成员i初始化, 不必在前面加static main() { int *count=&myclass::i; myclass ob1,ob2,ob3,ob4; cout<<“ myclass::i= ”<<*count<<endl; return 0; }
通过指针访问类的静态成员函数 #include<iostream.h> class myclass { public: myclass() { ++i; } static int geti() { return i; } private: static int i; }; int myclass::i=0; main() { int (*get)()=myclass::geti; myclass ob1,ob2,ob3; cout<<"myclass∷i="<<(*get)()<<endl; return 0; }
c++中,可以创建类作用域下的变量指针,来存放类中静态的数据成员的地址嘛? 不可以,因为静态的数据成员要比类的普通成员提前初始化,并且不需要关联到特定的对象,因此不能使用类作用域指针去存放静态数据成员的地址。
友元
目的
在一个类中可以有公用的(public)成员和私有的(private)成员。在类外可以访问公用成员,只有本类中的函数可以访问本类的私有成员。
友元可以访问与其有好友关系的类中的私有成员。
友元机制是对类的封装机制的补充,利用此机制,一个类可以赋予某些函数访问它的私有成员的特权。
友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。
友元函数
定义
友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有、保护和公有成员。
特点
可以是不属于任何类的一般函数
也可以是另一个类的成员函数
友元函数没有this指针,它是通过入口参数(该类对象)来访问对象的成员的。
声明
位置
当前类体中
格式
函数名前加friend
定义
类体外
与一般函数相同
类体内
函数名前加friend
说明
友元函数虽然可以访问该类的私有成员,但它毕竟不是成员函数,因此,在类的外部定义友元函数时,不能在函数名前加上“类名::”。
友元函数一般带有一个该类的入口参数。因为友元函数不是类的成员函数,它没有this指针,所以它不能直接引用对象成员的名字,也不能通过this指针引用对象的成员,它必须通过作为入口参数传递进来的对象名或对象指针来引用该对象的成员。
例子
// friend functions #include <iostream> using namespace std; class Rectangle { int width, height; public: Rectangle() {} Rectangle (int x, int y) : width(x), height(y) {} int area() {return width * height;} friend Rectangle duplicate (const Rectangle&); }; Rectangle duplicate (const Rectangle& param) { Rectangle res; res.width = param.width*2; res.height = param.height*2; return res; } int main () { Rectangle foo; Rectangle bar (2,3); foo = duplicate (bar); cout << foo.area() << '\n'; return 0; }
友元函数可以是多个类的
#include<iostream.h> #include<string.h> class boy; //向前引用 class girl{ public: void init(char N[],int A); friend void prdata(const girl plg ,const boy plb); private: char name[25]; int age; }; void girl∷init(char N[],int A) { strcpy(name,N); age=A; } class boy { public: void init(char N[],int A); friend void prdata(const girl plg,const boy plb); private: char name[25]; int age; }; void boy∷init(char N[],int A) { strcpy(name,N); age=A; } void prdata(const girl plg,const boy plb) { cout<<"name: "<<plg.name<<"\n"; cout<<"age: "<<plg.age<<"\n"; cout<<"name: "<<plb.name<<"\n"; cout<<"age: "<<plb.age<<"\n"; } main() { girl G1,G2,G3; boy B1,B2,B3; G1.init("Zhang Hao",12); G2.init("Li Ying",13); G3.init("Wan Hong",12); B1.init("Chen Lin",11); B2.init("Wang Hua",13); B3.init("Bai Xiu",12); prdata(G1,B1); //调用友元函数prdata() prdata(G2,B2); //调用友元函数prdata() prdata(G3,B3); //调用友元函数prdata() return 0; }
友元函数也可以是另一个类中的成员函数
个类的成员函数也可以作为另一个类的友元,这种成员函数不仅可以访问自己所在类对象中的所有成员,还可以访问friend声明语句所在类对象中的所有成员。 这样能使两个类相互合作、协调工作,完成某一任务。 一个类的成员函数作为另一个类的友元函数时,必须先定义这个类。
#include <iostream> using namespace std; class Date; //对Date类的提前引用声明 class Time //定义Time类 { public: Time(int,int,int); void display(Date &); //display是成员函数,形参是Date类对象的引用 private: int hour; int minute; int sec; }; class Date //声明Date类 { public: Date(int,int,int); friend void Time∷display(Date &); private: int month; int day; int year; }; Time∷Time(int h,int m,int s) //类Time的构造函数 { hour=h; minute=m; sec=s; } void Time∷display(Date &d) { cout<<d.month<<″/″<<d.day<<″/″<<d.year<<endl; cout<<hour<<″:″<<minute<<″:″<<sec<<endl; } Date∷Date(int m,in d,int y) //类Date的构造函数 { month=m; day=d; year=y; } int main( ) { Time t1(10,13,56); //定义Time类对象t1 Date d1(12,25,2004); //定义Date类对象d1 t1.display(d1); //调用t1中的display函数,实参是Date类对象d1 return 0; }
友元类
不仅可以将一个函数声明为一个类的“朋友”,而且可以将一个类(例如B类)声明为另一个类(例如A类)的“朋友”。这时B类就是A类的友元类。友元类B中的所有函数都是A类的友元函数,可以访问A类中的所有成员。
格式
class Y { //… }; class X { //… friend Y; // 声明类Y为类X的友元类 //… }; 类Y的所有成员函数都是类X的友元函数
说明
友元的关系是单向的而不是双向的。
若类X是类Y的友元,但类Y不一定是类X的友元。
友元的关系不能传递。
即若类X是类Y的友元,类Y是类Z的友元,但类X不一定是类Z的友元。
例子
/ friend class #include <iostream> using namespace std; class Square; class Rectangle { int width, height; public: int area () {return (width * height);} void convert (Square a); }; class Square { friend class Rectangle; private: int side; public: Square (int a) : side(a) {} }; void Rectangle::convert (Square a) { width = a.side; height = a.side; } int main () { Rectangle rect; Square sqr (4); rect.convert(sqr); cout << rect.area(); return 0; }
利弊分析
关于友元利弊的分析: 面向对象程序设计的一个基本原则是封装性和信息隐蔽,而友元却可以访问其他类中的私有成员,不能不说这是对封装原则的一个小的破坏。但是它能有助于数据共享,能提高程序的效率,在使用友元时,要注意到它的副作用,不要过多地使用友元,只有在使用它能使程序精炼,并能大大提高程序的效率时才用友元。
对象成员
如果一个类的对象是另一个类的数据成员,则称这样的数据成员为对象成员。
例子
class A { //... }; class B { A a; //类A的对象a为类B的对象成员 public: //… };
初始化的问题
class X{ 类名1 对象成员名1; 类名2 对象成员名2; … 类名n 对象成员名n; }; 一般来说,类X的构造函数的定义形式为; X::X(形参表0):对象成员名1(形参表1),…, 对象成员名i(形参表i) ,…,对象成员名n(形参表n) { // …构造函数体 }
初始化例子
#include<iostream.h> class A{ public: A(int x1,float y1) { x=x1; y=y1; } void show() { cout<<"\n x="<<x<<" y="<<y; } private: int x; float y; }; class B{ public: B(int x1,float y1,int z1):a(x1,y1) { z=z1; } void show() { a.show(); cout<<" z="<<z; } private: A a; int z; }; main() { B b(11,22,33); b.show(); return 0; }
B(A a1,int z1):a(a1) { z=z1; } main() { A a(11,22); B b(a,33); b.show(); return 0; }
共用数据的保护
方法
const
一般形式
类型名 const 对象名[(构造实参表列)];
const 类型名 对象名[(构造实参表列)];
作用
定义为 const 的对象的所有数据成员的值都不能被修改
非常对象和常对象的比较。 #include<iostream.h> class Sample { public: int m; Sample(int i,int j){ m=i; n=j;} void setvalue(int i){ n=i; } void disply(){ cout<<"m="<<m<<endl; cout<<"n="<<n<<endl; } private: int n; }; void main() { Sample a(10,20); //若为:const Sample a(10,20); a.setvalue(40);//不能修改常对象的数据成员 a.m=30; //不能修改常对象的数据成员 a.disply();//常对象不能调用普通成员函数 }
定义为 const 的对象,只能调用const类型的成员函数,构造函数除外。
mutable成员
对数据成员声明为mutable(易变的)时,即使是const对象,仍然可以修改该数据成员值。
常对象成员
常数据成员
用 const 声明的常数据成员,其值是不能改变的。只能通过构造函数的参数初始化表对常数据成员进行初始化。
class Time { const int hour; // 声明hour为常数据成员 Time(int h):hour(h){} // 通过参数初始化表对常数据成员hour初始化 }; class Time { const int hour; // 声明hour为常数据成员 Time(int h) { hour = h; // 错误 } };
常成员函数
成员函数声明中包含const时为常成员函数。此时,该函数只能引用本类中的数据成员,而不能修改它们(注意:可以改mutable成员),即成员数据不能作为语句的左值。
void show_Time( ) const; // 注意const的位置在函数名和括号之后 void Time::show_Time const { cout << hour << minute << sec << endl; }
注意:const是函数类型的一部分,在声明函数和定义函数时都要有const关键字,在调用时不必加const。
使用说明
如果在一个类中,有些数据成员的值允许改变,另一些数据成员的值不允许改变,则可以将一部分数据成员声明为const,以保证其值不被改变,可以用非const的成员函数引用这些数据成员的值,并修改非const数据成员的值。
如果要求所有的数据成员的值都不允许改变,则可以将所有的数据成员声明为const,或将对象声明为const(常对象),然后用const成员函数引用数据成员,这样起到“双保险”的作用,切实保证了数据成员不被修改。
如果已定义了一个常对象,则只能调用其中的const成员函数,而不能调用非const成员函数(不论这些函数是否会修改对象中的数据)。如果需要访问对象中的数据成员,可将常对象中所有成员函数都声明为const成员函数,但应确保在函数中不修改对象中的数据成员。
指向常对象的指针
一般形式
const 类型 *指针变量名
例子
const char *pc; // pc 指向的 char 是const型的 *pa = 'a'; // 错误:pa 指向的目标不能改变 pa++; // 正确,pa 本身的值可以改变
Time t1; const Time *pt; *pt = t1; // 错误:pt指向的目标不能改变
说明
指向常对象(变量)的指针变量,不能通过它来改变所指向目标对象的值,但指针变量的值是可以改变的。
如果被声明为常对象(变量),只能用指向常对象(变量)的指针变量指向它,而不能非const型指针变量去指向它。 const int a = 10, b = 20; const int *pa = &a; // 正确 int *pb = &a; // 错误:非 const 型指针
指向常对象(变量)的指针变量除了可以指向常对象(变量)外,还可以指向未被声明为const的对象(变量)。此时不能通过此指针变量改变该变量的值。 pa = &b; // 正确:也可指向非 const 型的变量 *pa = 30; // 错误:指向 const 型的指针不能改目标值
指向常对象(变量)的指针变量可以指向const和非const型的对象(变量),而指向非const型变量的指针变量只能指向非const的对象(变量)。 const int *pa = &b; // 正确 int *pb = &a; // 错误
如果函数的形参是指向非const型变量的指针,实参只能用指向非const变量的指针,而不能用指向const变量的指针,这样,在执行函数的过程中可以改变形参指针变量所指向的变量(也就是实参指针所指向的变量)的值。
如果函数形参是指向const型变量的指针,允许实参是指向const变量的指针,或指向非const变量的指针。
void f(Time *pt); Time *p1; const Time*p2; f(p1); // 正确 f(p2); // 错误 void g(const Time *pt) Time *p1; Const Time *p2; f(p1); // 正确 f(p2); // 正确
基本规则:希望在调用函数时对象的值不被修改,就应当把形参定义为指向常对象的指针变量,同时用对象的地址作实参(对象可以是const或非const型)。如果要求该对象不仅在调用函数过程中不被改变,而且要求它在程序执行过程中都不改变,则应把它定义为const型
对象的常引用
在C++中,经常用常引用作函数参数。这样既能保证数据安全,使数据不能被随意修改,在调用函数时又不必建立实参的拷贝,可以提高程序运行效率。
例子
class Time { public: Time(int,int,int); int hour; int minute; int sec; }; Time::Time(int h,int m,int s) { hour = h; minute = m; sec = s; } void fun1(Time &t) // t 是对象引用 { t.hour = 18; } void fun2(const Time &t) // t 为常引用 { t.hour = 18; // 错误 } int main( ) { void fun1(&); void fun2(const Time &); Time t1(10,13,56); fun(t1); cout<<t1.hour<<endl; return 0; }
分文件编写
一个源程序可以划分为多个源文件:
类声明文件(.h文件)
类实现文件(.cpp文件)
类的使用文件(main()所在的.cpp文件)
利用工程来组合各个文件。
例子
利用类表示一个堆栈(stack),并为此堆栈建立push()、pop()及显示堆栈内容的showstack()等函数
//文件1 stack.h #include <iostream.h> #include <iomanip.h> #include <ctype.h> const int SIZE=10; class stack { int stck[SIZE]; // 数组,用于存放栈中数据 int tos; // 栈顶位置(数组下标) public: stack(); void push(int ch); // 将数据ch压入栈 int pop(); // 将栈顶数据弹出栈 void ShowStack(); };
// 文件2 stack.cpp #include <iostream.h> #include "stack.h" stack∷stack() // 构造函数,初始化栈 { tos= 0; } void stack∷push(int ch) { if(tos==SIZE) { cout<<"Stack is full"; return; } stck[tos]=ch; tos++; cout<<"You have pushed a data into the stack!\n"; } int stack∷pop() { if (tos==0) { cout<<"Stack is empty"; return 0; } tos--; return stck[tos]; } void stack∷ShowStack() { cout<<"\n The content of stack: \n" ; if (tos==0) { cout<<"\nThe stack has no data!\n"; return; } for (int i=tos-1; i>=0;i--) cout<<stck[i]<<" "; cout<<"\n\n"; }
//文件3 stackmain.cpp #include <iostream.h> #include "stack.h" main() { cout<<endl; stack ss; int x; char ch; cout<<" <I> ------ Push data to stack\n"; cout<<" <O> ------ Pop data from stack\n"; cout<<" <S> ------ Show the content of stack\n"; cout<<" <Q> ------ Quit... \n"; while (1) { cout<<"Please select an item: "; cin>>ch; ch=toupper(ch); switch(ch) { case 'I': cout<<"\n Enter the value that "<<"you want to push: "; cin >>x; ss.push(x); break; case 'O': x=ss.pop(); cout<<"\n Pop "<<x<<" from stack.\n"; break; case 'S': ss.ShowStack(); break; case 'Q': return 0; default: cout<<"\n You have inputted a wrong item! Please try again!\n"; continue; } } }
嵌套类
定义
在一个类中定义的类称为嵌套类,定义嵌套类的类称为外围类。
目的
隐藏类名,减少全局的标识符,从而限制用户能否使用该类建立对象。这样可以提高类的抽象能力,并且强调了两个类(外围类和嵌套类)之间的主从关系。
说明
从作用域的角度上看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类的作用域内使用该类名时,需要加名字限定。
从访问权限的角度来看,嵌套类名与它的外围类的对象成员名具有相同的访问权限规则。不能访问嵌套类的对象中的私有成员函数,也不能对外围类的私有部分中的嵌套类建立对象。
嵌套类中的成员函数可以在它的类体外定义。
嵌套类中说明的成员不是外围类中对象的成员,反之亦然。嵌套类的成员函数对外围类的成员没有访问权,反之亦然。因此,在分析嵌套类与外围类的成员访问关系时,往往把嵌套类看作非嵌套类来处理。
在嵌套类中说明的友元对外围类的成员没有访问权。
如果嵌套类比较复杂,可以只在外围类中对嵌套类进行说明,关于嵌套类的详细的内容可在外围类体外的文件域中进行定义。