类与对象
1.类的声明
以时钟类为例
class Clock
{
int Hour,Minute,Second;
void SetTime(int h,int m,int s);
void ShowTime();
};
实现类的访问控制
数据封装的目的是信息隐蔽,通过设置成员的访问控制属性来实现对类成员的访问控制,这些控制属性有:public(公有成员)、protected(保护成员)、private(私有成员),所以可以对之前的程序做出以下修改:
class Clock
{
private:
int Hour,Minute,Second;
public:
void SetTime(int h,int m,int s);
void ShowTime();
};
2.类的实现
class Clock
{
private:
int Hour,Minute,Second;
public:
void SetTime(int h,int m,int s)
{
Hour=h;
Minute=m;
Second=s;
}
void ShowTime()
{
cout<<"Current Time:";
cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}
};//即将函数声明写在类里面
/*当然,也可以不改变原来的结构,将函数声明写在类的外部*/
class Clock
{
private:
int Hour,Minute,Second;
public:
void SetTime(int h,int m,int s);
void ShowTime();
};
void Clock::SetTime(int h,int m,int s)
{
Hour=h;
Minute=m;
Second=s;
}
void Clock::ShowTime()
{
cout<<"Current Time:";
cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}
3.实例分析
数组类的实现:赋值,求最大最小值;
#include <bits/stdc++.h>
using namespace std;
class IntArray
{
int *data;
int size=10;
public:
void setArray(int len);
int getSize();
int SetVal(int pos, int val);
int getMaxVal();
int getMinVal();
};
void IntArray::setArray(int len)
{
size=len;
data=new int[size];
}
int IntArray::getSize()
{
return size;
}
int IntArray::SetVal(int pos, int val)
{
if ((pos <= 0) || (pos > size))
return -1;
data[pos] = val;
return 0;
}
int IntArray::getMaxVal()
{
int temp = data[1];
for (int i = 1; i <= size; i++)
{
if (data[i] > temp)
temp = data[i];
}
return temp;
}
int IntArray::getMinVal()
{
int temp = data[1];
for (int i = 1; i <= size; i++)
{
if (data[i] < temp)
temp = data[i];
}
return temp;
}
int main()
{
IntArray array;
int size,value,max,min;
size=array.getSize();
cout<<"please input "<<size<<" members"<<endl;
array.setArray(10);
for(int i=1;i<=size;i++)
{
cin>>value;
array.SetVal(i,value);
}
max=array.getMaxVal();
min=array.getMinVal();
cout<<max<<endl;
cout<<min<<endl;
return 0;
}
4.对象引用
形式:
CLock C1(8,20,20);
Clock &Cr=C1;
Cr.showTime();
对象引用通常用作函数的参数,它不仅有对象指针的优点,而且比对象指针更简洁,更方便,更直观。
5.构造函数
- 构造函数是类的一种特殊的成员函数,本质上也是类的成员函数;
- 函数名和类名相同,可以有参数,但没有返回类型;
- 当创建类的一个新对象时,构造函数被自动调用,完成对象的初始化工作;
实现构造函数的方式:1.赋值语句的方式
class Clock
{
int Hour,Minute,Second;
public:
Clock(int h,int m,int s)
{
Hour=h;
Minute=m;
Second=s;
}
};
2.表达式表的方式
class Clock
{
int Hour,Minute,Second;
public:
Clock(int h,int m,int s):Hour(h),Minute(m),Second(s)
{
}
};
重载构造函数
-
一个类可以提供多个构造函数,即构造函数的重载;
-
重载的目的是为了满足不同的初始化需求
class Clock { private:int Hour,Minute,Second; public: Clock(int j,int m,int s); Clock(); Clock(char *timestr); }; void main() { Clock clock1(23,12,0); Clock clock2(); Clock clock3("14:45:32"); }
6.析构函数
- 与构造函数相对的是析构函数,C++通过析构函数来处理对象被销毁时的清理工作;
- 析构函数没有返回类型,没有参数,函数名是在类名前加“~”,析构函数会在对象的生存期结束后被自动调用;
class CString
{
private: int len;
char *buf;
public:CString(int n);
~CString();
void copy(char *src);
};
CString::CString(int n)
{
len=n;
buf=new char[n];
}
void CString::copy(char *src)
{
strcpy(buf,src);
}
CString::~CString()
{
delete []buf;
}
viod func()
{
CString obj(64);
obj.copy("helloworld");
}
void main()
{
func();
}
动态内存管理容易出错
-
删除动态内存失败
-
读写已删除的对象
-
对同一个内存空间使用两次delete:(1)2个及以上的指针指向同一个动态分配的对象;
(2)一个指针,多次删除
7.拷贝构造函数
如果将与自己同类的对象的引用作为参数进行构造函数的初始化时,该构造函数就称为拷贝构造函数,它将一个已经创建好的对象作为参数,根据需要将该对象中的数据成员逐一对应地赋值给新对象,示例如下:
class Point
{
private:
float x,y;
public:
Point(float a,float b)
{
x=a;y=b;
}
Point(Point &obj)
{
x=obj.x;
y=obj.y;
}
}
void main()
{
Point obj1(5,15);
Point obj2(obj1);
Point obj3=obj2;//两种调用拷贝构造函数的方式
}
缺省的拷贝构造函数使用位拷贝的方法来完成对象到对象的复制(即完全相同)
使用缺省拷贝构造函数的缺点:如果使用指针,拷贝后两个类的指针指向同一个空间,就会出现析构时同一块内存空间delete两次的错误
故:通过自定义拷贝构造函数可以准确地复制数据,以免发生错误
8.静态数据成员的定义和初始化
定义静态数据成员
class ABCD//定义静态数据成员
{
int value;
public:
static int s_value;//在类内声明静态数据成员
};
int ABCD::s_value=6;//在类外定义静态数据成员,在定义时给初值
静态数据成员属于类而不属于对象,所以无论建立多少个该类的对象,静态数据成员只有一份拷贝
静态成员函数类似
静态成员的应用:如果有一个类MyClass,利用静态成员可以实现程序运行过程中只能有一个实例;
class MyClass
{
private:
static MyClass *instance;
MyClass(){}//将构造函数声明为非public属性,可以控制对象的生成
public:
static MyClass *getInstance()
{
if(instance==NULL)
{
instance=new MyClass();
}
return instance;
}
};
MyClass *MyClass::instance=NULL;
void main()//设计模式——单件模式
{
MyClass *obj1,*obj2;
obj1=MyClass::getInstance();
obj2=MyClass::getInstance();//两次调用,只有一个实例
}
9.友元
(1)友元函数
- 友元函数可以直接访问私有成员
- 友元函数的声明可以放在类内的任意位置,且和普通函数一样可以直接调用友元函数
- 友元函数不属于任何类,因此没有this指针
#include <bits/stdc++.h>
using namespace std;
class Point
{
private:
int x,y;
public:
Point(float a,float b):x(a),y(b){}
friend float Distance(Point a,Point b);//类内定义:前面加上friend
};
float Distance(Point a,Point b)//若在类外声明,声明方式与普通函数相同
{
float dx,dy;
dx=a.x-b.x;//在友元函数内可以直接访问类的私有成员
dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
int main()
{
Point p1(3.0,5.0);
Point p2(4.0,6.0);
float d=Distance(p1,p2);//调用方式与普通函数相同
cout<<d<<endl;
return 0;
}
(2)友元类:
1.除了将一个普通函数声明为一个类的友元函数外,也可以将一个类Y声明为另一个类X的友元类;
2.友元类的特点:类Y中的所有成员函数都成为类X的友元函数,都能直接访问类X中的所有成员。
#include <bits/stdc++.h>
using namespace std;
class Y;//向前说明
class X
{
int x;
friend class Y;
public:
void show()
{
cout<<x;
}
};
class Y
{
public:
void SetX(X &obj,int v)//Y中的成员函数可以自由访问X中的所有成员
{
obj.x=v;
}
};
int main()
{
X xobj;
Y yobj;
yobj.SetX(xobj,5);
xobj.show();//输出5
return 0;
}
(3)将成员函数说明为另一个类的友元函数
class X;//向前说明
class Y
{
public:
void SetX(X &obj,int v);
void func(X &obj){};
};
Class X
{
friend void Y::SetX(X &obj,int v);
};
void Y::SetX(X &obj,int v)
{
obj.x=v;
}
10.类的组合
实例:Circle的实现
#include <bits/stdc++.h>
using namespace std;
class Point
{
float x,y;
public:
Point(float xx,float yy)
{
x=xx;y=yy;
}
float GetX(){return x;}
float GetY(){return y;}
void moveto(float xx,float yy)
{
x=xx;
y=yy;
}
};
class Circle
{
Point center;
float radius;
public:
Circle(float x,float y,float r):center(x,y)//下面详细说明
{
radius=r;
}
void moveto(float xx,float yy)
{
center.moveto(xx,yy);
}
};
int main()
{
Circle acircle(0,0,5);
acircle.moveto(5,8);
return 0;
}
//注释处:若子对象对应的类的构造函数有参数,那么包含该子对象的类必须使用表达式的方式先初始化子对象