1.类的定义
类是对具有相同属性和行为的一类客观事物的概括描述。是用户自定义的数据类型(程序设计语言角度)
类的定义包括行为和属性两个部分。属性以数据表示,行为通过函数。
C++类定义的格式
class 类名
{
public:
公有数据成员和成员函数;
protected:
保护数据成员和成员函数;
private:
私有数据成员和成员函数;
};// 注意分号不能省略!!!
各成员函数的实现
注意事项:
- 类的数据成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。
- 类定义必须以分号“;”结束。
- 类与结构体的区别:
没有明确指定类成员的访问权限时,C++结构体的成员是 公有的,而类的成员是私有的
2.类成员访问
public 公有成员 类的外部接口
protected 保护成员 仅允许本类成员函数及派生类成员函数访问
private 私有成员 仅允许本类成员函数访问
3.成员函数
类的成员函数是实现类的行为属性的成员。
一般将成员函数声明为函数原型,在类外具体实现成员函数。
成员函数的定义:
返回值类型 类名::成员函数名(参数表)
{
函数体
}
4.对象
对象是类的实例或实体。
类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
对象的定义
格式如下 :
类名 对象名1,对象名2,…,对象名n;
定义对象应注意:
必须在定义了类之后,才可以定义类的对象。
5.类成员的访问
对象成员的访问包括:
1. 圆点访问形式:对象名.公有成员
2. 指针访问形式
指针访问形式:
对象指针变量名->公有成员
类定义和使用时应注意:
- 在类的定义中不能对数据成员进行初始化。
- 类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
- 类中的数据成员可以是C++语法规定的任意数据类型。
- 类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
- 类定义必须以分号“;”结束
- class与struct的不同:
class中,成员缺省情况是private。
struct中,成员缺省情况是public。
内联函数
内联函数作用:
减少频繁调用小子程序的运行的时间开销
内联函数机制:
编译器在编译时,将内联函数的调用以相应代码代替
内联函数声明:
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; }
int main()
{
Date a_date,b_date(2014,3,25);
a_date.showDate();
b_date.showDate();
return 0;
}
(2) 利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:
类名 *指针变量 = new 类名[(实参表)];
例如:
Date *date1=new Date(1998,4,28);
就创建了对象(*date1)。
int main()
{
Date *date1;
date1=new Date(1998,4,28);
// 以上两条语句可合写成:Date *date1=new Date(1998,4,28);
cout<<"Date1 output1:"<<endl;
date1->showDate();
delete date1;
return 0;
}
构造函数的初始化列表
--------数据成员的初始化
构造函数初始化成员有两种方法:
A.使用构造函数的函数体进行初始化
B.使用构造函数的初始化列表进行初始化
使用构造函数的函数体进行初始化:
class Date
{
int d, m, y;
public:
Date(int dd, int mm, int yy)
{
d=dd;
m=mm;
y=yy;
}
Date(int dd, int mm)
{
d=dd;
m=mm;
}
}
使用构造函数的初始化列表进行初始化:
格式:
funname(参数列表):初始化列表
{函数体,可以是空函数体}
初始化列表的形式:
成员名1(形参名1),成员名2(形参名2),成员名n(形参名n)
使用初始化成员列表的形式:
class Date{
int d, m, y;
public:
Date(int dd, int mm, int yy):d(dd),m(mm),y(yy)
{ }
Date(int dd, int mm): d(dd),m(mm)
{ }
}
必须使用参数初始化列表对数据成员进行初始化的几种情况:
1、数据成员为常量
2、数据成员为引用类型
3、数据成员为没有无参构造函数的类的对象
数据成员为常量,数据成员为引用类型:
#include <iostream>
using namespace std;
class A{
public:
A(int i):x(i),rx(x),pi(3.14)
{}
void display()
{cout<<"x="<<x<<"rx="<<rx<<"pi="<<pi<<endl;}
private:
int x,℞
const float pi;
};
int main(){
A aa(10);
aa.display();
return 0;
}
数据成员为没有无参构造函数的类的对象:
#include<iostream>
using namespace std ;
class A
{ public :
A ( int x ) : a ( x ) { }
int a ;
} ;
class B
{ public :
B( int x, int y ) : aa( x ), b( y ) { }
void out()
{ cout << "aa = " << aa.a << endl << "b = " << b << endl ; }
private :
int b ;
A aa ;
} ;
int main ()
{ B objB ( 3, 5 ) ;
objB . out ( ) ;
}
类成员的初始化的顺序:
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关
#include <iostream>
using namespace std;
class CMyClass{
public:
CMyClass(int x, int y):m_y(x),m_x(m_y)
{
cout<<"m_x="<<m_x<<endl;
cout<<"m_y="<<m_y<<endl;
}
private:
int m_x,m_y;
};
int main()
{
CMyClass mc(15,10);
return 0;
}
运行结果:
m_x=-858993460
m_y=15
带默认值的构造函数
#include <iostream>
using namespace std;
class Box
{
public:
Box(); //定义了全部带默认值的构造函数,不能再定义无参 构造函数
Box(int h=10,int w=10 , int l=10); //只能在声明时指定默认值
int volume();
private:
int height,width, length;
};
Box::Box(int h, int w,int l):height(h),width(w),length(l)
{}
int Box::volume(){
return width*length*height;
}
int main()
{
Box box1;
cout<<"The volume is "<<box1.volume();
Box box2(12,30,25);
cout<<"The volume is "<<box2.volume();
Box box3(30,25);
cout<<"The volume is "<<box3.volume();
return 0;
}
构造函数的调用
class Person
{
public:
Person (unsigned x):age(x){}
void setAge( unsigned n ){ age = n; }
unsigned getAge( ) { return age; }
private:
unsigned age;
};
int main()
{
Person a(20); //此时没有无参构造函数,出错。
Person b[60]
…….
构造函数一般被定义为公有成员。
析构函数
对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间,析构函数可以完成上述工作。
析构函数自动调用(隐式调用)
析构函数没有返回值,不能有参数,也不能重载
定义格式如下(类外实现):
类名::~类名()
{
函数语句
}
析构函数有以下一些特点:
① 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
② 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;
③ 当撤消对象时,编译系统会自动地调用析构函数。
默认析构函数
若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。
系统自动生成的默认构造函数形式如下:
类名::~类名(){}
一般情况下,可以不定义析构函数。但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收。
this指针
需要显示使用this指针的三种情况:
(1)在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用 return *this,返回本对象的地址时,return this。
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。
(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。
复制构造函数
复制构造函数用一个已有同类对象创建新对象进行数据初始化,C++为类提供默认版本的复制构造函数。
程序员可以定义用户版本的复制构造函数
语法形式
类名 :: 类名(const 类名 & 引用名 , …);
特点:
(1)复制构造函数名与类名相同,并且也没有返回值类型。
(2)复制构造函数可写在类中,也可以写在类外。
(3)复制构造函数要求有一个类类型的引用参数。
(4)如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。
以下三种情况下由编译系统自动调用复制构造函数:
(1)声明语句中用类的一个已知对象初始化该类的另一个对象时。
(2)当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
(3)当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。
浅复制与深复制
关于浅复制:
●在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容
●默认复制构造函数所进行的是简单数据复制,即浅复制
关于深复制:
●通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。
●自定义复制构造函数所进行的复制是浅复制。
定义支持深复制的复制构造函数
1、深复制构造函数必须显式定义
2、深复制构造函数的特点:
(1)定义:类名::类名([const] 类名 &对象名);
成员变量的处理:对复杂类型的成员变量,使用new操作符进行空间的申请,然后进行相关的复制操作。
常成员
常数据成员是指数据成员在实例化被初始化后,其值不能改变。
在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
常对象
如果在说明对象时用const修饰,则被说明的对象为常对象。
常对象的说明形式如下:
类名 const 对象名[(参数表)];
或
const 类名 对象名[(参数表)];
在定义常对象时必须进行初始化,而且不能被更新。
说明:
(1)C++不允许直接或间接更改常对象的数据成员。
(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
常成员函数
在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:
类型说明符 函数名(参数表) const;
const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)
静态成员
类成员冠以static声明时,称为静态成员。
静态数据成员为同类对象共享。
静态成员函数与静态数据成员协同操作。
静态成员不属于某一个单独的对象,而是为类的所有对象所共有。
静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员。
对于类的普通数据成员,每一个对象都各自拥有一个副本。(分配不同的存储空间)
对于静态数据成员,每个类只拥有一个副本 。(在静态存储区分配一个存储空间,对所有对象都是可见的)
公有访问权限的静态成员,可以通过下面的形式进行访问:
(1)类名::静态成员的名字
(2)对象名.静态成员名字
(3)对象指针->静态成员的名字
(4)在静态成员函数内部,直接访问。
静态数据成员声明及初始化:
在类外进行静态数据成员的声明
类型 类名::静态数据成员[=初始化值]; //必须进行声明
不能在成员初始化列表中进行初始化
如果未进行初始化,则编译器自动赋初值(默认值是0)
初始化时不能使用访问权限
静态成员函数
除静态数据成员以外,一个类还可以有静态成员函数。
静态函数仅可以访问静态成员,或是静态成员函数或是静态数据成员。
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
静态成员函数没有this指针,只能对静态数据操作
定义静态成员函数的格式如下:
static 返回类型 静态成员函数名(参数表);
与静态数据成员类似,调用公有静态成员函数的一般格式有如下几种:
(1)类名::静态成员函数名(实参表)
(2) 对象. 静态成员函数名(实参表)
(3)对象指针->静态成员函数名(实参表)
说明:
(1)静态成员函数在类外定义时不用static前缀。
(2)静态成员函数主要用来访问同一类中的静态数据成员。
(3) 私有静态成员函数不能在类外部或用对象访问。
(4)可以在建立对象之前处理静态数据成员。
( 5)编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
(6)静态成员函数中是没有this指针的。
(7)静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。
友元函数
如果在本类(类A)以外的其他地方定义了一个函数(函数B),这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数,在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。
友元函数(函数B)可以访问这个类(类A)中的私有成员
class A
{ private:
int i ;
friend void FriendFun(A * , int) ;
public:
void MemberFun(int) ;
} ;
…
void FriendFun( A * ptr , int x )
{ ptr -> i = x ; } ;
void A:: MemberFun( int x )
{ i = x ; } ;
//作用相同
A Aobj ;
FriendFun( &Aobj, 5 ) ;
Aobj.MemberFun( 5 ) ;
友元类
假设有如下的类的
class B
{
……………
private:
A obj1;
};
A是另一个类,B类的成员函数要访问A的私有成员,此时,需要在A类中,把类B声明为A的友元。
若F类是A类的友元类,则F类的所有成员函数都是A类的友元函数
友元类通常设计为一种对数据操作或类之间传递消息的辅助类
友元类的声明格式:
friend class 类名;
#include<iostream>
using namespace std ;
class A
{ friend class B ;
public :
void Display()
{ cout << x << endl ; } ;
private :
int x ;
} ;
class B
{ public :
void Set ( int i ) { Aobject . x = i ; }
void Display () { Aobject . Display () ; }
private :
A Aobject ;
} ;
int main()
{ B Bobject ;
Bobject . Set ( 100 ) ;
Bobject . Display () ;
}
类的包含(类的组合)
类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类 “抄”进来。
当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;
对象成员的初始化
出现成员对象时,该类的构造函数要包含对象成员的初始化。如果构造函数的成员初始化列表没有对成员对象初始化时,则使用成员对象的无参(缺省)构造函数。
建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数。
成员对象的构造函数调用次序和成员对象在类中的说明次序一致(声明顺序为:a1、b1、b2),与它们在成员初始化列表中出现的次序无关(初始化列表顺序为:b1、b2、a1)。
析构函数的调用顺序相反
#include<iostream>
using namespace std;
class A
{ public:
A(int x):a(x){ }
int a ;
};
class B
{ public:
B( int x, int y ) : aa(x) { b = y ; }
void out() { cout<<"aa = "<<aa.a<<endl<<"b = "<<b<<endl ; }
private:
int b ;
A aa ;
} ;
int main()
{ B objB( 3, 5 ) ;
objB.out() ;
}
管理信息系统开发基本步骤
- 了解系统要实现什么功能?
- 对系统功能进行分类汇总
- 设计操作类
- 一个成员函数实现一个具体功能;
- 设确定数据成员,设计构造函数;
- 设计数据类
- 整合操作类所需要的数据、设计数据类
- 明确如何修饰数据成员、确定数据类的成员函数
- 编码调试数据类
- 在main函数中创建数据类对象、调用各成员函数
- 调试操作类
- 先调试最急需的成员函数;
- 再陆续增加成员函数
- 设计多组测试数据,用不同的测试数据反复运行程序