一、类的定义
1.类是对具有相同属性和行为的一组对象与统一描述。是用户自定义的数据类型。
2.类的定义包括行为和属性两个部分。
3.属性以数据表示,行为通过函数实现。
二、c++类定义的格式
class 类名
{
public:公有数据成员和成员函数;
protected:保护数据成员和成员函数;
private:私有数据成员和成员函数;
};<=分号不得省略!!!
各成员函数的实现;
三、类的举例
class Student //定义学生类Student
{
public: //声明类成员
void Getinfo(string pname, string pid
,charSex, int a,double s);
void modify(float s);
void display();
private:
string name;
stringid;
char sex;
int age;
double score;
}; //类定义以分号结束
注意事项:
●类的成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。
●类定义必须以分号“;”结束。
●类与结构体的区别:没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。
2.访问控制
类的访问属性有:
● Public:公有成员,类的外部接口。
● Protected:保护成员,仅允许本类成员函数及派生类成员函数访问。
● Private:私有成员,仅允许本类成员函数访问。
3.成员函数
类的成员函数是实现类的行为属性的成员。
一般将成员函数声明为函数原型,在类外具体实现成员函数。
成员函数的定义
返回值类型 类名::成员函数名(参数表)
{
函数体
}
class Point
{
public:
float GetPointx();
float GetPointy();
void InitPoint(float PointA_x, float PointA_y);
void Move(float New_x, float New_y);
private:
float P1_x,P1_y;
voidPoint::InitPoint(float PointA_x, float PointA_y)
{
P1_x=PointA_x;
P1_y=PointA_y;
}
void Point::Move(floatNew_x, float New_y)
{
P1_x+=New_x;
P1_y+=New_y;
}
float Point::GetPointx()
{
return P1_x;
}
4.对象
●对象是类的实例或实体。
●类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
定义对象时应注意:
●必须在定义了类之后,才可以定义类的对象。
5.类成员的访问
对象成员的访问包括:
●圆点访问形式:对象名.公有成员
●指针访问形式
指针访问形式:
对象指针变量名->公有成员
#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;
};
例题:
写一个程序,输入矩形的长和宽,输出面积和周长。
将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数”
class CRectangle
{
public:
int w,h;
int Area() {
return w * h;
}
int Perimeter() {
return 2 * ( w + h);
}
void Init( int w_,int h_ ) {
w = w_; h = h_;
}
}; //必须有分号
C++中,类的名字就是用户自定义的类型的名字。可以象使用基本类型那样来使用它。
CRectangle 就是一种用户自定义的类型
类定义和使用时应注意:
在类的定义中不能对数据成员进行初始化。
类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
类中的数据成员可以是C++语法规定的任意数据类型。
类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
类定义必须以分号“;”结束
class与struct的不同:
class中,成员缺省情况是private。
struct中,成员缺省情况是public。
成员函数可以重载:
函数重载:函数名相同,但参数不相同(类型不同,或者个数不同)的一组函数。
编译器根据不同参数的类型和个数产生调用匹配
函数重载用于处理不同数据类型的类似任务
举例:
参数个数相同参数类型不同
#include<iostream>
using namespace std ;
int abs ( int a ) ;
double abs ( double f ) ;
int main ()
{ cout<< abs ( -5 ) << endl ;
cout << abs ( -7.8 ) << endl;
}
int abs ( int a )
{ return a < 0 ? -a : a; }
double abs ( double f )
{ return f < 0 ? -f : f ; }
四、构造函数与析构函数
构造函数是用于创建对象的特殊成员函数
当创建对象时,系统自动调用构造函数
构造函数的作用是:
为对象分配空间;对数据成员赋初值;请求其他资源
没有用户定义的构造函数时,系统提供缺省版本的构造函数
构造函数名与类名相同:类名
构造函数可以重载
构造函数可以有任意类型的参数,但没有返回类型
析构函数是用于取消对象的成员函数
当一个对象作用域结束时,系统自动调用析构函数
析构函数的作用是进行对象消亡时的清理工作
没有用户定义析构函数时,系统提供缺省版本的析构函数
析构函数名为: ~ 类名
析构函数没有参数,也没有返回类型
构造函数:
什么是构造函数?通俗的讲,在类中,函数名和类名相同的函数称为构造函数。它的作用是在建立一个对象时,作某些初始化的工作(例如对数据赋予初值)。C++允许同名函数,也就允许在一个类中有多个构造函数。如果一个都没有,编译器将为该类产生一个默认的构造函数。
构造函数上惟一的语法限制是它不能指定返回类型,甚至void 也不行。
不带参数的构造函数:一般形式为 类名 对象名(){函数体}
带参数的构造函数:不带参数的构造函数,只能以固定不变的值初始化对象。带参数构造函数的初始化要灵活的多,通过传递给构造函数的参数,可以赋予对象不同的初始值。一般形式为:构造函数名(形参表);
创建对象使用时:类名 对象名(实参表);
构造函数名(参数=缺省值,参数=缺省值,……);
析构函数:
当一个类的对象离开作用域时,析构函数将被调用(系统自动调用)。析构函数的名字和类名一样,不过要在前面加上 ~ 。对一个类来说,只能允许一个析构函数,析构函数不能有参数,并且也没有返回值。析构函数的作用是完成一个清理工作,如释放从堆中分配的内存。
一个类中可以有多个构造函数,但析构函数只能有一个。对象被析构的顺序,与其建立时的顺序相反,即后构造的对象先析构。
举例:
Student::Student(){
cout<<"您好呀! 新同学,你叫什么名字?"<<endl;
}
Student::Student(std::stringn,int chScore,int enScore,intmathScore):name(n),ch(chScore),en(enScore),math(mathScore){
cout<<"您好呀!"<<name<<",很高兴见到你!"<<endl;
}
Student::~Student(){
cout<<"再见!"<<name<<"."<<endl;
}
void Student::setChScore(intscore){
Student::ch = score;
}
voidStudent::setName(std::string n){
Student::name = n;
}
voidStudent::setEnScore(int score){
en = score;
}
voidStudent::setMathScore(int score){
math = score;
}
void Student::show(){
Student::count();
ios_base::fmtflags orig =cout.setf(ios_base::fixed,ios_base::floatfield);
std::streamsize prec = cout.precision(1);
cout<<name<<" 同学的语文成绩为"<<ch<<"分,数学成绩为"<<math<<"分,英语成绩为"<<en<<"分,平均成绩"<<average<<"分"<<endl;
cout.setf(orig,ios_base::floatfield);
}
五、this指针
用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配存储单元
需要显式引用this指针的三种情况:
(1)在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用 return *this,返回本对象的地址时,return this。
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。
(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针
#include<iostream>
using namespace std;
class Location
{
int X,Y; //默认为私有的
public:
voidinit(int x,int y) { X =x; Y = y;};
void assign(Location& pointer);
int GetX(){ return X; }
int GetY(){ return Y; }
};
voidLocation::assign(Location& pointer)
{
if(&pointer!=this) //同一对象之间的赋值没有意义,所以要保证pointer不等于this
{ X=pointer.X; Y=pointer.Y; }
}
int main(){
Location x;
x.init(5,4);
Location y;
y.assign(x);
cout<<"x.X= "<< x.GetX()<<" x.Y = "<<x.GetY();
cout<<"y.X= "<< y.GetX()<<" y.Y = "<<y.GetY();
return0;
}
六、复制构造函数
复制构造函数用一个已有同类对象创建新对象进行数据初始化
C++为类提供默认版本的复制构造函数
程序员可以定义用户版本的复制构造函数
语法形式
类名:: 类名(const 类名 & 引用名 , …);
复制构造函数的特点:
1、复制构造函数名与类名相同,并且也没有返回值类型。
2、复制构造函数可写在类中,也可以写在类外。
3、复制构造函数要求有一个类类型的引用参数。
4、如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。
(一)、常函数
常数据成员是指数据成员在实例化被初始化后,其值不能改变。
在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
使用const说明的数据成员称为常数据成员。
如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值
如果在说明对象时用const修饰,则被说明的对象为常对象。
常对象的说明形式如下:
类名 const 对象名[(参数表)];
或者
const 类名 对象名[(参数表)];
在定义常对象时必须进行初始化,而且不能被更新。
说明:
(1)C++不允许直接或间接更改常对象的数据成员。
(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
(二)、常成员函数
在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:
类型说明符 函数名(参数表)const;
const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)
七、静态成员
类成员冠以static声明时,称为静态成员。
静态数据成员为同类对象共享。
静态成员函数与静态数据成员协同操作。
(一)、静态成员函数
静态成员不属于某一个单独的对象,而是为类的所有对象所共有
静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员
对于类的普通数据成员,每一个对象都各自拥有一个副本。(分配不同的存储空间)
对于静态数据成员,每个类只拥有一个副本 。(在静态存储区分配一个存储空间,对所有对象都是可见的)
公有访问权限的静态成员,可以通过下面的形式进行访问
类名::静态成员的名字
对象名.静态成员名字
对象指针->静态成员的名字
在静态成员函数内部,直接访问
静态数据成员声明及初始化:
在类外进行静态数据成员的声明
类型 类名::静态数据成员[=初始化值]; //必须进行声明
不能在成员初始化列表中进行初始化
如果未进行初始化,则编译器自动赋初值(默认值是0)
初始化时不能使用访问权限
(二)、静态成员函数
除静态数据成员以外,一个类还可以有静态成员函数。
静态函数仅可以访问静态成员,
或是静态成员函数或是静态数据成员。
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
静态成员函数没有this指针,只能对静态数据操作
定义静态成员函数的格式如下:
static 返回类型 静态成员函数名(参数表);
与静态数据成员类似,调用公有静态成员函数的一般格式有如下几种:
类名::静态成员函数名(实参表)
对象. 静态成员函数名(实参表)
对象指针->静态成员函数名(实参表)
注意:
(1)静态成员函数在类外定义时不用static前缀。
(2)静态成员函数主要用来访问同一类中的静态数据成员。
(3) 私有静态成员函数不能在类外部或用对象访问。
(4)可以在建立对象之前处理静态数据成员。
(5)编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
(6)静态成员函数中是没有this指针的。
#include<iostream>
using namespace std;
class M
{
public:
M(int a) { A=a; B+=a;}
static void f1(M m);
private:
int A;
static int B;
};
void M::f1(M m)
{
cout<<“A=”<<m.A<<endl;//静态成员函数中通过对象来引用非静态成员
cout<<“B=”<<B<<endl;
}
int M::B=0; //静态数据成员初始化的格式<数据类型><类名>::<静态数据成员名>=<值>
void main()
{
M P(5),Q(10);
M::f1(P); //静态成员函数调用时不用对象名
M::f1(Q);
}
八、类的包含/类的组合
类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类 “抄”进来。
当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
构造函数 ( 形参表 ) : 对象成员1(形参表 ) , …, 对象成员n (形参表 ) ;
(一)、对象成员初始化
出现成员对象时,该类的构造函数要包含对象成员的初始化。如果构造函数的成员初始化列表没有对成员对象初始化时,则使用成员对象的无参(缺省)构造函数。
建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数。
举例
#include<iostream>
#include<cmath>
using namespacestd;
class Point{
private:
int x, y;
public:
Point(int a = 0, int b = 0)
{
x = a; y = b;
cout << "Pointconstruction: " << x << ", "<< y <<endl;
}
Point(Point &p) // copyconstructor,其实数据成员是int类型,默认也是一样的
{
x = p.x;
y = p.y;
cout << "Point copyconstruction: " << x << ", "<< y <<endl;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
};
class Line{
private:
Point start, end;
public:
Line(Point pstart, Pointpend):start(pstart), end(pend) // 组合类的构造函数对内前对象成员的初始化必须采用初始化列表形式
{
cout << "Lineconstructior " << endl;
}
float getDistance()
{
double x =double(end.getX()-start.getX());
double y =double(end.getY()-start.getY());
return (float)(sqrt)(x*x+y*y);
}
};
int main()
{
Point p1(10,20),p2(100,200);
Line line(p1,p2);
cout << "The distance is:"<<line.getDistance()<<endl; return 0;
}
创建组合类的对象,构造函数的执行顺序:先调用内嵌对象的构造函数,然后按照内前对象成员在组合类中的定义顺序,与组合类构造函数的初始化列表顺序无关。然后执行组合类构造函数的函数体。析构函数调用顺序相反。
上例中当创建Point类对象p1, p2时调用Point类构造函数2次;当创建组合类Line对象line时,调用了组合类Line的构造函数(首先是参数:需要实参传递给形参,需要调用Point类的拷贝构造函数2次,注意参数传递顺序是从右到左;然后是初始化列表:再调用Point类的拷贝构造函数2次,分别完成内前对象成员start, end的初始化);然后才开始执行Line类的构造函数的函数体。
输出:
[cpp] view plaincopy
Point construction: 10,20
Point construction: 100,200
Point copy construction:100, 200
Point copy construction:10, 20
Point copy construction:10, 20
Point copy construction:100, 200
Line constructior
The distance is:201.246
组合类的拷贝构造函数:系统也可以自动添加,自动调用内嵌对象的拷贝构造函数,对各个内嵌对象成员进行初始化。
[cpp] view plaincopy
Line(Line&ll):start(ll.start), end(ll.end)
{
cout << "Line copy constructor" << endl;
}
(二)、对象数组
所谓对象数组是指每一数组元素都是对象的数组。
定义一个一维对象数组的格式如下:
类名 数组名[下标表达式];
(三)、成员对象数组的初始化
成员对象的初始化可在构造函数初始化列表中进行,推想对于成员对象数组的初始化,也可以在本类构造函数使用初始化列表:
例
#include<iostream>
using namespace std;
class A{
public:
A(int i = 0):x(i) {}
int x;
};
class B
{
public:
B():a[0](0),a[1](1) {} //error C2059: 语法错误: “[”
A a[2];
};
int main()
{
B b;
cout<<b.a[0].x<<endl;
cout<<b.a[1].x<<endl;
return 0;
}
#include<iostream>
using namespace std;
class A{
public:
A(){ cout<<"Hello A."<<endl; }
A(int i):x(i) { cout<<"x ="<<x<<endl; }
int x;
};
class B{
public:
B() //成员对象数组已经通过A的默认构造函数初始化了,下面是进行赋值
{
a[0] = A(0);
a[1] = A(1);
}
A a[2];
};
无法通过初始化成员列表对对象数组进行初始化
九、简单的成绩管理系统的实现
定义一个Student 类
该类对象所共有的属性
姓名,学号,数学成绩,英语成绩,计算机,平均成绩,学生名次
该类对象的操作
初始化一个对象(输入一个学生的相关信息)
输出每个对象的信息
修改信息
等等
个人感想:
这么多天与c++接触,不会的东西太多,不了解的也太多,我知道这不是一门简单的学科,我也曾想过放弃,但后来我想,既然是我选择的我就必须要坚持下去,我会加油把这门学科学好!