一.知识归纳
1.类
定义:类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型。类的定义包括行为和属性两个部分,属性以数据表示,行为通过函数实现。
格式:
class 类名
{
public:
公有数据成员和成员函数;
protected:
保护数据成员和成员函数;
private:
私有数据成员和成员函数;
};
各成员函数的实现;
注意事项:
1.类的成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。
2.类定义必须以分号“;”结束。
3.类与结构体的区别:
没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。
2.成员函数:
定义:
返回值类型 类名::成员函数名(参数表)
{
函数体
}
eg:
class Point
{
public:
floatGetPointx();
floatGetPointy();
voidInitPoint(float PointA_x, float PointA_y);
voidMove(float New_x, float New_y);
private:
float P1_x,P1_y;
};
void Point::InitPoint(float PointA_x, floatPointA_y)
{
P1_x=PointA_x;
P1_y=PointA_y;
}
void Point::Move(float New_x, float New_y)
{
P1_x+=New_x;
P1_y+=New_y;
}
float Point::GetPointy()
{
returnP1_y;
}
float Point::GetPointx()
{
returnP1_x;
}
float GetPointx()
{
return P1_x;
}
float GetPointy()
{
return P1_y;
}
3.对象
定义:
类名 对象名1,对象名2,…,对象名n;
注:
1.对象是类的实例或实体。
2.类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
3.必须在定义了类之后才可以定义类的对象。
eg:
class Point
{
public:
voidInitPoint(float PointA_x, float PointA_y);
voidMove(float New_x, float New_y);
floatGetPointx();
floatGetPointy();
private:
float P1_x,P1_y;
};
int main()
{
Point p1,p2;
}
Ø 类成员的访问:
1. 圆点访问形式:对象名.公有成员
2.指针访问形式:对象指针变量名->公有成员
eg:
#include<iostream.h>
class ptr_access
{
public:
voidsetvalue(float a, float b) { x=a; y=b; }
float Getx() {return x;}
float Gety() {return y;}
voidprint()
{
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;
}
类定义和使用时应注意:
- 在类的定义中不能对数据成员进行初始化。
- 类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
- 类中的数据成员可以是C++语法规定的任意数据类型。
- 类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
- 类定义必须以分号“;”结束
- class与struct的不同:
① class中,成员缺省情况是private。
② struct中,成员缺省情况是public。
4.内联函数
作用:减少频繁调用小子程序的运行的时间开销。
声明: inline 函数原型
Ø 注:内联函数仅在函数原型作一次声明。
适用于只有1 ~ 5行的小函数
不能含有复杂结构控制语句 ,不能递归调用
5.函数重载
定义:函数名相同,但参数不相同(类型不同,或者个数不同)的一组函数。
(用于处理不同数据类型的类似任务)
eg:
#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; }
(参数个数不同)
#include<iostream>
using namespace std ;
int max ( int a , int b ) ;
int max ( int a , int b, int c ) ;
int main ()
{cout << max ( 5, 3 ) << endl ;
cout << max (4, 8, 2 ) << endl ;
}
int max ( int a , int b )
{return a > b ? a : b ; }
int max ( int a , int b, int c )
{int t ;
t= max ( a , b ) ;
return max ( t , c ) ;
}
6.构造函数
定义:构造函数是用于创建对象的特殊成员函数
(当创建对象时,系统自动调用构造函数)
作用:
为对象分配空间;对数据成员赋初值;请求其他资源。
注:
没有用户定义的构造函数时,系统提供缺省版本的构造函数。
构造函数名与类名相同:类名。
构造函数可以重载。
构造函数可以有任意类型的参数,但没有返回类型。
Ø 默认构造函数:如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象。
形式:
类名::类名()
{
}
(默认构造函数是一个空函数)
Ø 构造函数创建对象的两种方法:
1.利用构造函数直接创建对象.其一般形式为:
类名 对象名[(实参表)];
(这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数)
eg:
int main()
{
Date *date1;
date1=new Date(1998,4,28);
// 以上两条语句可合写成:Date*date1=new Date(1998,4,28);
cout<<"Date1output1:"<<endl;
date1->showDate();
delete date1;
return 0;
}
2.利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:
类名 *指针变量= new 类名[(实参表)];
例如:
Date *date1=new Date(1998,4,28);
就创建了对象(*date1)。
Ø 数据成员的初始化
1.使用构造函数的函数体进行初始化
eg:
class Date
{
intd, m, y;
public:
Date(intdd, int mm, int yy)
{
d=dd;
m=mm;
y=yy;
}
Date(intdd, int mm)
{
d=dd;
m=mm;
}
}
2.使用构造函数的初始化列表进行初始化
格式:
funname(参数列表):初始化列表
{ 函数体,可以是空函数体 }
形式:
成员名1(形参名1),成员名2(形参名2),成员名n(形参名n)
eg:
class Date
{
intd, m, y;
public:
Date(intdd, int mm, int yy):d(dd),m(mm),y(yy)
{ }
Date(intdd, int mm): d(dd),m(mm)
{ }
}
Ø 必须使用参数初始化列表对数据成员进行初始化的几种情况
1.数据成员为常量
2.数据成员为引用类型
3.数据成员为没有无参构造函数的类的对象
Ø 类成员的初始化的顺序:
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关。
7.析构函数
定义:对象生存期结束时,做清理工作,比如:释放成员(指针)所占有的存储空间。
(析构函数没有返回值,不能有参数,也不能重载)
格式(类外实现):
类名::~类名()
{
函数语句
}
特点:① 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
②析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;
③当撤消对象时,编译系统会自动地调用析构函数。
Ø 默认析构函数
若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。
系统自动生成的默认构造函数形式如下:
类名::~类名(){}
一般情况下,可以不定义析构函数
但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收
8.this指针
定义:用来指向不同的对象。
一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
Ø 需要显式引用this指针的三种情况:
(1)在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用return *this,返回本对象的地址时,return this。
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。
(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。
9.复制构造函数
定义:用一个已有同类对象创建新对象进行数据初始化
语法形式:
类名 :: 类名(const 类名 & 引用名 , …);
特点:
1.复制构造函数名与类名相同,并且也没有返回值类型。
2.复制构造函数可写在类中,也可以写在类外。
3.复制构造函数要求有一个类类型的引用参数。
4.如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。
调用:
1.声明语句中用类的一个已知对象初始化该类的另一个对象时。
2.当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
3.当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。
Ø 浅复制与深复制
关于浅复制:
●在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容
●默认复制构造函数所进行的是简单数据复制,即浅复制
关于深复制:
●通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。
●深复制构造函数的特点:
1.定义:类名::类名([const] 类名 &对象名);
2.成员变量的处理:对复杂类型的成员变量,使用new操作符进行空间的申请,然后进行相关的复制操作
10.类的其他成员
Ø 常成员
1.常数据成员:使用const说明的数据成员称为常数据成员。
如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。
2.常对象:如果在说明对象时用const修饰,则被说明的对象为常对象。
常对象的说明形式如下:
类名 const 对象名[(参数表)];
或者
const 类名 对象名[(参数表)];
在定义常对象时必须进行初始化,而且不能被更新。
说明:
(1)C++不允许直接或间接更改常对象的数据成员。
(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
3.常成员函数:在类中使用关键字const说明的函数为常成员函数。
格式:
类型说明符函数名(参数表) const;
const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)
Ø 静态成员
1.静态数据成员:
静态数据成员在定义或说明时前面加关键字static,如:
class A
{
int n;
staticint s;
};
公有访问权限的静态成员,可以通过下面的形式进行访问:
类名::静态成员的名字
对象名.静态成员名字
对象指针->静态成员的名字
在静态成员函数内部,直接访问。
2.静态成员函数:
格式:
static 返回类型 静态成员函数名(参数表);
调用公有静态成员函数的一般格式有如下几种:
类名::静态成员函数名(实参表)
对象. 静态成员函数名(实参表)
对象指针->静态成员函数名(实参表)
说明:
(1)静态成员函数在类外定义时不用static前缀。
(2)静态成员函数主要用来访问同一类中的静态数据成员。
(3) 私有静态成员函数不能在类外部或用对象访问。
(4)可以在建立对象之前处理静态数据成员。
(5)编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
(6)静态成员函数中是没有this指针的。
(7)静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。
11.友元函数
定义:
如果在本类(类A)以外的其他地方定义了一个函数(函数B),这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数,在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。
友元函数(函数B)可以访问这个类(类A)中的私有成员
12.类的包含:定义一个新的类时,通过编译器把另一个类“抄”进来。
构造函数( 形参表) : 对象成员1(形参表 ) , … , 对象成员n (形参表) ;
Ø 对象成员的初始化:
1.出现成员对象时,该类的构造函数要包含对象成员的初始化。如果构造函数的成员初始化列表没有对成员对象初始化时,则使用成员对象的无参(缺省)构造函数。
2.建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数。
3.成员对象的构造函数调用次序和成员对象在类中的说明次序一致(声明顺序为:a1、b1、b2),与它们在成员初始化列表中出现的次序无关(初始化列表顺序为:b1、b2、a1)。
4.析构函数的调用顺序相反。
13.对象数组:指每一数组元素都是对象的数组。
格式:
类名 数组名[下标表达式];
Ø 对象数组的初始化
1. 当对象数组所属的类中包含带参的构造函数,可用初始化列表完成对象数组的初始化。
eg:
int main(){
Pointp1[4];
Pointp2[4]={5,6,7,8};
Point p3[4]={Point(9,10),Point(11,12),Point(13,14),Point(15,16)};
Pointp4[4]={Point(17,18),Point(10,20)};
Pointp5[2][2]={21,22,Point(23,24)};
}
2. 当对象数组所属的类中包含无参的构造函数,也可以先定义,再给每个数组元素赋值
eg:
int main()
{
Pointp1[4];
p1[0]=Point(1,1);
}
3.当对象数组所属的类中包含单个参数的构造函数,可简写。
eg:
int main()
{
Pointp1[4];
Pointp2[4]={5,6,7,8};
//Pointp2[4]={Point(5), Point(6), Point(7), Point(8)};
Point p3[4]={Point(9,10),Point(11,12),Point(13,14),Point(15,16)};
Pointp4[4]={Point(17,18),Point(10,20)};
Pointp5[2][2]={21,22,Point(23,24)};
}
二.心得体会
类和对象不同于我们上学期的学习内容,它是一个新的概念,同时也是一种新的解题方法。
1.上学期我们主要考虑怎么解决问题,而这学期学了类之后我们要先考虑解决一个问题需要哪些数据,然后该怎么组织应用这些数据。
2.比起上学期学到的,类的框架更为简单清楚:定义类+主函数。这就为我们调试程序提供了简便,最高效率的调试方法便是写完一个类便调通。
3.对于一些简单问题来说可能运用类相对麻烦,但当我们解决更为复杂的问题时,用类来解决的效率便要比其他方式高多了。
4.同时个人觉得想要利用类写好一个系统最主要的分析的逻辑,只有搞清楚你所写系统的内部结构,各部分关系,才能写出行之有效的环环相扣可运行的系统。这点对于自己来说可谓是路漫漫其修远兮。