一.基本内容总结
(一)基础知识
1.类的定义:
类是对具有相同属性和行为的一组对象的抽象和统一描述。是用户自定义的数据类型。/类的定义包括行为和属性两部分。/属性以数据表示,行为通过函数实现。
C++类定义的说明语句一般形式为:
class<类名>
{
public:
公有段数据成员和成员函数;
protected:
保护段数据成员和成员函数;
private:
私有段数据成员和成员函数;
};
*分号不可掉。
除了class以外,关键字struct也可以用于定义类。用struct定义类时,若不特别指出,则所有成员是共有的。例如,一个日期类的说明如下:
class Date
{
public:
void SwtDate(int y,int m,int d);
int IsLeapYear();
void PrintDate();
private:
int year,month,day;
};
Date类有三个私有数据成员:year,month和day。Date还有三个公有成员函数:SetDate用于获取对象的值,设置日期;IsLeapYear用于判断是否是闰年;PrintDate函数用于输出日期。
*成员函数在类外定义使用作用域区分符进行说明,此时函数头的形式为:
返回类型 类名::函数名(参数表)
{
函数体 }
*注意事项
1.类的成员可以是其他人的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。2.类定义必须以分号“;”结束。3.类与结构体的区别:没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。
2.访问对象成员:
*对象是类的实例或实体;类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
对象的定义(格式如下):
类名 对象1,对象2,.....,对象名n;
定义对象时应注意,必须在定义了类之后,才可以定义类的对象。
例如,访问对象的公有成员:
#include<iostream>
using namespace std;
class Tclass
{ public:
int x,y;
void print()
{ cout<<x<<","<<y;};
};
int main()
{ Tclass test;
test.x=100;
test.y=200;
test.print();
}
*类成员的访问,对象成员的访问包括:
圆点访问形式:对象名.公有成员和指针访问形式(对象指针变量名->公有成员)
例如:
#include<iostream.h>
class ptr_access {
public:
void setvalue(float a, float b) { x=a; y=b; }
float Getx() {return x;}
float Gety() {return y;}
void print()
{
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
private: //私有数据成员
float x,y;
};
int main()
{
float a1,a2;
ptr_access *ptr=new ptr_access;
ptr->setvalue(2,8);
//通过指针访问公有成员函数
ptr->print();
a1=(*ptr).Getx();
//通过公有成员函数访问私有数据成员
a2=(*ptr).Gety();
cout<<"a1="<<a1<<endl;
cout<<"a2="<<a2<<endl;
return 0;
}
3.内联函数:
内联函数作用:减少频繁调用小子程序的运行时间的开销;
内联函数机制:编译器在编译时,将内联函数的调用以相应代码代替;
内联函数声明:inline函数原型;
注:内联函数仅在函数原型做一次声明;适用于只有1-5行的小函数;不能含有复杂结构控制语句,不能递归调用。
(二).简单构造函数和析构函数
1.原型:
类名::类名(参数表);
类名::~类名();
例如,为类Date建立一个构造函数:
#include <iostream.h>
class Date {
public:
Date(); // 无参构造函数
Date(int y,int m,int d);
void showDate();
private:
int year, month, day;
};
Date::Date() // 构造函数的实现
{ year=0; month=0; day=0; }
Date::Date(int y,int m,int d)
{ year=y; month=m; day=d; }
inline void Date::showDate()
{ cout<<year<<"."<<month<<"."<<day<<endl; }
2.带参数的构造函数:
带参数的函数构造函数可以在建立一个对象时,用指定的数据初始化对象的数据成员。
3.重载构造函数:
构造函数与普通函数一样,允许重载。如果Date类具有多个构造函数,创建对象时,将根据参数匹配调用其中一个。例如:
class Date
{ public:
Date();
Date(int);
Date(int,int);
Date(int,int,int);
~Date();
//......
};
//......
void f()
{ Date d; //调用 Date();
Date d1(2000); //调用 Date(int);
Date d1(2000,1); //调用 Date(int,int);
Date d1(2000,1,1); //调用 Date(int,int,int);
}
像所以C++函数一样,构造函数可以具有默认参数。但是,定义默认参数构造函数时,要注意调用时可能产生二义性。例如:
class X
{ public:
X();
X(int i=0);
//...
};
void f()
{ X one(10); //正确
X two; //错误
}
上述程序在说明X类对象one时,调用构造函数X::X(int i=0);但在说明two时,系统无法判断调用X::X()还是X::X(int i=0),因此会出现匹配二义性。
4.复制构造函数:
创造对象时,有时希望用一个已有的同类型对象的数据对它进行初始化。C++可以完成类对象数据的简单复制。用户自定义的复制构造函数用于完成更为复杂的操作。
复制构造函数要求有一个类类型的引用函数:
类名::类名(const 类名&引用名,......);
为了保证所引用的对象不被修改,通常把引用参数说明为const参数。例如:
class A
{ public:
A(int);
A(const A&,int=1); //复制构造函数
//......
};
//......
A a(1); //创建对象a,调用A(int)
A b(a,0); //创建对象b,调用A (const A&,int=1)
A c=b; //创建对象c,调用A (const A&,int=1)
第一条语句创建对象a,调用一般的构造函数A(int)。第二条和第三条语句都调用了复制构造函数,复制对象a创建b,复制对象b创建c,展示了调用复制构造函数的两种典型方法。
*调用复制构造函数的时机:
当说明语句建立对象时,可以调用复制构造函数进行数据初始化。另外,当函数具类类型传值数或者函数返回类类型值时,都需要调用复制构造函数,完成局部对象的初始化工作。
如果函数具有类类型传值参数,那么,调用该函数时,首先要调用复制构造函数,用实际参数对象的值初始化形式参数对象。
5.析构函数:
对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间;
析构函数可以完成上述工作;
析构函数自动调用(隐式调用),析构函数没有返回值,不能有参数,也不能重载。
定义格式如下(类外实现):
类名::~类名()
{
函数语句
}
*析构函数有以下特点:
1.析构函数与构造函数名字相同,但它前面必须加一个波浪号(~); 2. 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只有一个析构函数; 3.当撤销对象时,编译系统会自动的调用析构函数。
6.this指针:
用类定义对象时,系统会为每一个对象分配储存空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配储存空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配储存单元。
#include <iostream>
using namespace std;
class Time
{
public:
int hour;
int minute;
int sec;
void setHour( ){ hour=10;}
};
int main( )
{ cout<<sizeof(Time)<<endl; return 0; }
7.深复制和浅复制:
当类的数据成员是简单的数据类型时,创建对象时的数据复制系统机制工作得很好。但是,如果数据成员资源是由指针指示的堆,系统复制对象数据时只进行指针(地址值)复制,而不会重新分配内存空间。这样,程序运行时会产生对象操作异常;另外,当对象作用域结束后,又会错误地重复释放堆。这种情况下,需要使用用户自定义的复制构造函数。
*浅复制:
在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制;
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容;
默认复制构造函数所进行的是简单数据复制,即浅复制 。
*深复制:
通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制;
自定义复制构造函数所进行的复制是浅复制。
(三)类的其他成员
1.常成员:
在类中,定义常成员用const约束。常数据成员是指数据成员在实例化被初始化后约束为只读;常成员函数是指成员函数的this指针被约束为指向常量的常指针,在函数体不能修改数据成员的值。
(1)常数据成员:
在C++类定义中,const可以约束基本类型的数据成员为常数据成员。因为类对象要通过执行构造函数才能建立存储空间,所以,用构造函数实现数据成员值的初始化是必需的。在C++语言中,使用构造函数参数初始化对常数据成员进行初始化。
**数据成员可以在构造函数中直接用常量进行初始化,这样,每个对象建立的常数据成员都有相同的值。
**外一种对常数据成员进行初始化的方法是,使用带参数的构造函数,创建对象时,用实际参数对常数成员赋值。这样,每个对象的常数据成员就可以有不同的初始值。
(2)常对象:
若在定义对象的说明语句以const作前缀,则该对象称为常对象。这个对象的全部数据成员作用域中约束为只读。
(3)常成员函数:
在类中使用关键字const说明的函数为常成员函数,格式如下:
类型说明符 函数名(参数表)const;
*const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const;常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数和构造函数除外)。
#include<iostream>
using namespace std ;
class Simple
{ int x, y ;
public :
void setXY ( int a, int b) { x = a ; y = b ; }
void printXY() { cout << x << "," << y << endl ; }
void constFun ( ) const
{ x ++ ; y ++ ; }//非法
};
2.静态成员:
(1).静态数据成员:
静态数据成员在定义或说明时前面加关键字static,如:
class A
{
int n;
static int s;
};
(2).静态成员函数:
除静态数据成员以外,一个类还可以有静态成员函数;
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名;
静态成员函数没有this;指针只能对静态数据操作。
3.友元:
*用友元函数计算两点间距离:
#include<iostream>
using namespace std ;
#include<math.h>
class Point
{ public:
Point(double xi, double yi) { X = xi ; Y = yi ;}
double GetX() { return X ; }
double GetY() { return Y ; }
friend double Distance ( Point & a, Point & b ) ;
private: double X, Y ;
} ;
double Distance(Point & a, Point & b )
{ double dx = a.X - b.X ;
double dy = a.Y - b.Y ;
return sqrt ( dx * dx + dy * dy ) ;
}
int main()
{ Point p1( 3.0, 5.0 ) , p2( 4.0, 6.0 ) ;
double d = Distance ( p1, p2 ) ;
cout << "This distance is " << d << endl ;
}
4.类的包含:
*构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;
*出现成员对象时,该类的构造函数要包含对象成员的初始化。如果构造函数的成员初始化列表没有对成员对象初始化时,则使用成员对象的无参(缺省)构造函数。建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数。
*用类包含计算两点间距离:
#include<cmath>
#include<iostream>
using namespace std;
class Point
{ public:
Point( int xi=0, int yi=0 ) { x = xi; y = yi; }
int GetX() { return x; }
int GetY() { return y; }
private: int x; int y;
};
class Distance
{ public:
Distance( Point xp1, Point xp2 );
double GetDis() { return dist; }
private:
Point p1, p2;
double dist;
};
二.例题总结
如下为student类的一个实例,第一次尝试,还有些许错误和待优化的地方:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
class student
{
string name;
int no;
int score[3];
float average;
int order;
public:
student(int id,string na,int x,int y,int z):name(na),no(id)
{
score[0]=x;
score[1]=y;
score[2]=z;
order=1,average=(score[0]+score[1]+score[2])/3;
}
student()
{
score[0]=score[1]=score[2]=0;
order=1,average=0;
}
int getNo(){return no;}
float getAverage(){return average;}
void setAverage(int avg){average=avg;}
void setOrder(int x){order=x;}
int getOrder(){return order;}
string getName(){return name;}
void setName(string name){this->name=name;}
void display();
};
void student::display()
{
cout<<name<<"\t"<<no<<"\t"<<score[0]<<"\t"<<score[1]<<"\t"<<score[2]<<"\t"<<average<<"\t"<<order<<endl;
}
bool cmp1(student stu1,student stu2)
{
if(stu1.getAverage()-stu2.getAverage()>=1e-9)return 1;
else return 0;
}
bool cmp2(student stu1,student stu2)
{
return stu1.getNo()<stu2.getNo();
}
class student_list
{
student list[60];
int n;
public:
student_list():n(0){};
void add();
void deleteStu();
void query();
void change();
void display(int flag);
void menu();
int search(int no);
void sortList();
};
void student_list::add()
{
int no,x,y,z;
string name;
system("cls");
cout<<"按学号·姓名·数学·英语·c++顺序输入学生信息,学号输入-1表示结束"<<endl;
while((cin>>no)&&no!=-1)
{
cin>>name>>x>>y>>z;
student s(no,name,x,y,z);
list[n++]=s;
sortList();
}
}
void student_list::sortList()
{
sort(list,list+n,cmp1);
int i;
for(i=0;i<n;i++)
list[i].setOrder(i+1);
}
void student_list::display(int flag)
{
if(flag)sort(list,list+n,cmp2);
else sort(list,list+n,cmp1);
cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<"名次"<<endl;
for(int i=0;i<n;i++)
list[i].display();
}
int student_list::search(int no)
{
int i;
for(i=0;i,n;i++)
if(list[i].getNo()==no)
return -1;
}
void student_list::query()
{
int no,i;
system("cls");
cout<<"请输入要查询同学的学号,按-1结束查询";
while(cin>>no&&no!=-1)
{
i=search(no);
if(i!=-1)
{
cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<"名次"<<endl;
list[i].display();
cout<<"请输入要查询同学的学号,按-1结束查询";
}
else
cout<<"学号输入有误,请重输,输入-1结束查询"<<endl;
}
}
int main()
{
student s(1,"ff",66,77,88);
s.display();
cout<<s.getAverage()<<endl;
s.setName("方法");
s.display();
student_list c;
c.add();
c.display(1);
c.query();
c.display(0);
c.change();
c.display(1);
c.deleteStu();
c.display(1);
return 0;
}
三.学习心得
学习本章内容,主要是思维的转变,由之前的面向过程编译到面向对象设计。本章主要是对设计管理系统的初步认知,即通过对方需求来找到系统设计的初步框架,再从框架中的各个分类来细化需求,为需求分类。当然,最重要的是把各类操作与需求转化成数据并加以修饰,这是本章的关键;最后,加以调试和修改,使之具备增添和删减的灵活性,以完成思维的转变。随着学习的深入,最后的目的是对软件的设计,相信会越来越难。