1.类:将抽象的到的数据和行为(或功能)相结合,形成一个有机整体,也就是将数据与操作数据的函数代码进行有机的结合,形成“类”,其中的数据和函数都是类的成员。也就是说类中包含两个部分:成员变量,成员函数。
类的一般形式:// class 关键字 + 类名 (遵循标识符命名规范)。例如:
class Clock //时钟类
{
public(访问控制修饰符): //外部接口
void setTime(int newH,int newM,int newS); //行为,代码成员
void showTime(); //行为,代码成员
private: //特定的访问权限 ;私有成员
int hour,minute,second; //属性。数据成员
}
关键字 public,private,protected(保护型成员) 是用来指定成员的不同访问权限。
上面示例封装了时钟的数据和行为,分别称为Clock类的数据成员和函数成员。类中的成员函数可以直接访问类中的成员变量,并且私有成员只能在类内被访问,公有成员在类内类外都可以访问。
2.对象:类的对象是该类的某一特定实体(实例),声明一个对象和声明一个一般变量相同,
一般形式: 类名 + 对象名 ; 例如:Clock myClock(声明了一个时钟类型的对象myClock)。
通过定义类和对象,就可以访问对象的成员。采用“.”操作符。
访问数据成员一般形式: 对象名.数据成员
调用函数成员的一般形式:对象名.函数成员名(参数表)。
3.成员函数的实现:函数的原型声明要写在类体中,原型说明函数的参数表和返回值类型,类的实现写在类定义之外
*注意(实现成员函数一定要指明类的名称)。
形式:
返回值类型 类名 :: 函数成员名(参数表)
{
函数体
}
例如:
void Clock:: setTime(int newH,int newM,int newS)
{
hour=newH;
minute=newM;
second=newS;
}
void showTime()
{
cout<<hour<<":"<<miute<<":"<<second<<endl;
}
与普通函数不同,类的成员函数必须用类名来限制,“::”是作用域分辨符。
4.带默认形参值得成员函数:类的成员函数可以有形参值,调用规则同于普通函数。类成员函数的默认值,一般写在声明里(主调方只能看到声明,看不到定义),两边都可以写但是只能写一方。例如:
class Clock
{
public:
void setTime(int newH=0 ,int newM=0 ,int newS=0);
````
};
即若调用该函数时没有给出实参,就会按照默认形参值将闹钟设置到午夜零点。
5.内联成员函数:若有的成员函数需要被频繁调用,并且代码简单,则其可以被定义为内联函数。
两种声明方式:显示声明和隐式声明。
比如将时钟类的showTime()函数声明为内联函数:
隐式声明:
class Clock
{
public:
void setTime(int newH,int newM,int newS);
void showTime(){
cout<<hour<<":"<<miute<<":"<<second<<endl;
}
private:
int hour,minute,second;
}
显示声明:
inline void Clock::showTime(){
cout<<hour<<":"<<miute<<":"<<second<<endl;
}
6.构造函数(支持重载)
构造函数的作用:
-
创建类对象;
-
初始化其值;
构造函数所具有的的特性: -
构造函数的函数名与类名相同,而且没有返回值;
-
构造函数通常被声明为共有函数;
3)构造函数在对象被创建的时候将被自动调用
构造函数的执行顺序: -
传参;
-
按照类中成员变量声明顺序,依次构造其成员;
-
执行构造函数的函数体;
构造函数有两种重载形式:
1)带有参数的;
2)无参数的,即默认构造函数(调用时无须提供参数的构造函数);
例如:class Clock { public: Clock(int newH,int newM,int newS); //构造函数 Clock() //构造函数 void settime(int newH,int newM,int newS) void showtime(); private: int hour; int minute; int second; }; CLock:: Clock(int newH,int newM,int newS) //构造函数的实现 { hour=newH; minute=newM; second=newS; } Clock:: Clock() //构造函数 // 默认构造函数的实现 { hour=0; minute=0; second=0; } int main() { Clock c1(0,0,0); //调用有参数的构造函数 Clock c2; // 调用无参数的构造函数 } 注:自定义的构造函数带有形参,在建立对象时必须给出初始值;
7.复制构造函数(拷贝构造函数):
是一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类的对象的引用。
拷贝构造函数的作用:
使用一个已经存在的对象,去初始化同类的一个新对象;
拷贝构造函数的功能:
把初始值对象的每个数据成员的值都复制到新建立的对象中;
拷贝构造函数被调用的情况(重难点):
1)当用类的一个对象去初始化该类的另一个对象时;
2)如果函数的形参是类的对象,调用函数时;
3)如果函数的返回值是类的对象,返回函数值时,拷贝构造函数被调用
声明和实现拷贝构造函数的一般方法:
class 类名
{
public:
类名(形参表) //构造函数
类名(类名&对象名) //拷贝构造函数
…
};
类名::类名(类名&对象名) //拷贝构造函数的实现
{
函数体
}
例如:Point 类
class Point
{
public:
Point (int xx=0,int yy=0){
x=xx;
y=yy;
}
Point (Point &P) //拷贝构造函数
int getX(){return x;}
int getY(){reurn y;}
private:
int x,y;
};
Point ::Point(Point &P) //拷贝构造函数的实现
{
x=P.x;
y=P.y;
cout<<“calling the copy constructor”<<endl;
}
void fun1(Point P) //形参为Point类对象的函数
{
cout <<P.getX()<<endl;
}
void fun2() //返回值为Point类对象的函数
{
Point a(1,2);
return a;
}
int main()
{
Point a(4,5); //第一个对象
Point b(a); //用a初始化b。第一次调用拷贝构造函数
cout<<b.getX()<endl;
fun1(b); //对象b作为函数fun1的实参,第二次调用拷贝构造函数;
b=fun2(); //函数的返回值是类对象,函数返回时,调用拷贝构造函数
cout <<b.getX()<<endl;
return 0;
}
7.析构函数:同于构造函数,也是类的一个共有成员函数,由类名前加”~“构成
析构函数一般形式:**~ 类名*,*例如:~Clock() { }
析构函数作用:用来完成对象被删除前的一些清理工作,释放(删除)类对象;
析构函数的调用:是在对象的生存周期即将结束的时刻被自动调用的, 调用完成之后,对象也就随之消失,相应的内存空间也被释放
析构函数的执行顺序:
1)先执行析构函数的函数体;
2) 按照类中成员变量声明顺序的逆序,依次析构其成员;
析构函数的特性:
1)(不同于构造函数)析构函数不接收任何参数,但可以是虚函数;
2)没有返回值;
注:
1)析构函数没有重载函数,析构函数只有一个;
2)如果不进行显示说明,系统也会生成一个函数体为空的隐含析构函数;
例如:给时钟类加入一个空的内联析构函数,其功能和系统自动生成的隐含析构函数相同
class Clock
{
public:
Clock();
void setTime(int newH,int newM,int newS);
void showTime();
~Clock() { }
private:
int hour,minute,second;
};
8.类的组合:类的组合描述的是一个类内嵌其他类的对象作为成员的情况,他们之间的关系是一种包含与被包含的关系。
1)在创建类的对象时,若这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。所以在创建对象时既要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。
注:创建组和类时,不仅自身的构造函数的函数体被执行,而且还将调用其内嵌对象的构造函数。构造函数的调用顺序:
1>调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中的出现顺序。***内嵌对象在构造函数的初始化列表中出现的顺序与内嵌对象在构造函数的调用顺序无关。
2>执行本类构造函数函数体。
3>析构函数的调用执行顺序与构造函数刚好相反。析构函数的函数体被执行完毕后,内嵌对象的析构函数将被一一执行
2)组合类构造函数定义的一般形式:
类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),·····
{类的初始化}
注:
”内嵌对象1(形参表),内嵌对象2(形参表),·····“称作初始化列表;
其本质是初始化对象成员(调用嵌。成员对象的带参构造**);
书写位置:在构造函数的函数首部之后,函数体之前**。其书写顺序不影响构造函数 的执行顺序。
例如:
//例4-4 类的组合,线段(Line)类
//4_4.cpp
#include <iostream>
#include <cmath>
using namespace std;
//Point类定义
class Point
{
public:
Point(int xx = 0, int yy = 0)
{
x = xx;
y = yy;
}
Point(Point &p);
int getX() { return x; }
int getY() { return y; }
private:
int x, y;
};
//复制构造函数的实现
Point::Point(Point &p)
{
x = p.x;
y = p.y;
cout << "Calling the copy constructor of Point" << endl;
}
//类的组合
class Line //Line类的定义
{
public: //外部接口
Line(Point xp1, Point xp2);
Line(Line &l);
double getLen() { return len; }
private: //私有数据成员
Point p1, p2; //Point类的对象p1,p2
double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2)
{
cout << "Calling constructor of Line" << endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
//组合类的复制构造函数
Line::Line (Line &l): p1(l.p1), p2(l.p2)
{
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}
//主函数
int main()
{
Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
Line line(myp1, myp2); //建立Line类的对象
Line line2(line); //利用复制构造函数建立一个新对象
cout << "The length of the line is: ";
cout << line.getLen() << endl;
cout << "The length of the line2 is: ";
cout << line2.getLen() << endl;
return 0;
}