目录
C++类和对象的进阶
综述
C++类和对象的进阶:类对象初始化、对象数组、对象指针、共用数据的保护(const)、不同对象间的数据共享(static)、访问私有数据(friend)。
带举例,供学习、参考和查阅。
类对象初始化
变量初始化:声明变量时,同时初始化。
类对象初始化(数据成员初始化):在类中声明对象时,无法同时初始化,类不是实体,不占存储空间,显然无法容纳数据;需要公有成员函数(构造函数)初始化。
构造函数:由用户自定义的特殊成员函数,用来初始化类对象;其函数名为类名;没有返回类型,可被重载,一个类可有多个构造函数;类对象进入作用域时,系统自动调用构造函数。
构造函数实现类对象初始化,实例1:
class time{
private:int hour,minute,second;
public:
time()//声明构造函数,构造函数函数名和类名相同,无返回值
{
hour=0;minute=0;second=0;
}
void setTime();
void showTime();
};
void time::setTime()
{
cin>>hour;cin>>minute;cin>>second;
}
void time::showTime()
{
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
void main()
{
time t1;//定义类对象t1,调用构造函数t1.time
t1.setTime();t1.showTime();
time t2;//定义类对象t2,调用构造函数t2.time
t2.showTime();
}
//若看到函数名与类名一样,它一定是构造函数
带参数的构造函数:不用将每个对象初值置为相同。
声明一个构造函数的一般格式:构造函数名(类型1 形参1,类型2 形参2,…)。
定义一个对象的一般格式:类名 对象名(实参1,实参2,…)。
带参数的构造函数实现类对象初始化,实例2:
//两个长方体,长宽高为1,2,3和4,5,6,编写一个基于对象的程序,分别求他们的体积,要求用带参数的构造函数初始化对象
#include <iostream>
using namespace std;
class box{
private:int height,width,length;//三个数据成员
public:box(int h,int w,int len);//声明带参数的构造函数,三个形参
int volume();
};
box::box(int h,int w,int len)
{
height=h;width=w;length=len;//把三个形参赋给三个数据成员
}
int box::volume()
{
return height*width*length;
}
int main()
{
box box1(1,2,3);//定义对象,依次是类名、对象名,实参
cout<<"box1的体积为:"<<box1.volume()<<endl;
box box2(4,5,6);
cout<<"box2的体积为:"<<box2.volume()<<endl;
renturn 0;
}
参数初始化表对数据成员初始化:在类体函数头中实现数据成员初始化而不是在函数体内
参数初始化表实现类对象初始化,实例3:
//用参数初始化表对数据成员初始化,直接在类体中定义构造函数,更简练
#include <iostream>
using namespace std;
class box{
private:int height,width,length;
public:box(int h,int w,int len):height(h),width(w),lenghth(len){}//"{}"代表函数体
int volume(){returnheight*width*length;}
};
int main()
{
box box1(1,2,3);//依次是类名、对象名,实参
cout<<"box1的体积为:"<<box1.volume()<<endl;
box box2(4,5,6);
cout<<"box2的体积为:"<<box2.volume()<<endl;
renturn 0;
}
构造函数的重载:类中可以有多个构造函数,但参数表不同,方便同类对象初始化,实例4:
#include <iostream>
using namespace std;
class circle{
private:float radius;
public:circle();//声明无参数构造函数(参数表为空的构造函数叫默认构造函数)
circle(float r):radius(r){}//声明带参数构造函数
float area(){return radius*radius*3.14;}
};
circle::circle(){radius=10.0;}
int main()
{
circle c1(1.0),c2;
cout<<"c1面积为:"<<c1.area<<endl;
cout<<"c2面积为:"<<c2.area<<endl;
return 0;
}
使用默认参数的构造函数,和构造函数的重载互斥,实例5:
#include <iostream>
using namespace std;
class box{
private:int height,width,length;
public:Box(int h=10,int w=10,int len=10);//声明构造函数时指定默认参数
int volume();
}
box::box(int h,int w,int len):height(h),width(w),length(len){}//定义函数时,可以不指定默认参数
int Box::volume(){return(height*width*length);}
int main()
{
Box box1;//无给定实参
cout<<"the volume of box is"<<box1.volume()<<endl;
Box box2(15);//给定1实参
cout<<"the volume of box is"<<box2.volume()<<endl;
Box box3(15,30);//给定2实参
cout<<"the volume of box is"<<box3.volume()<<endl;
Box box4(15,30,20);//给定3实参
cout<<"the volume of box is"<<box4.volume()<<endl;
return 0;
}
对象数组
析构函数:撤销对象占用内存前进行清理工作,作用与构造函数相反;其函数名为类名;没有返回类型、没有函数参数、不能被重载,一个类只能有一个析构函数;对象生命结束时,自动执行。
什么时候调用析构函数?
1.函数中定义对象:函数调用结束释放对象,释放对象前自动执行析构函数。
2.static局部对象:函数调用结束时,包含的对象不会被释放,只在main函数结束或调用exit函数时,才调用static局部对象的析构函数。
3.全局对象:程序流程离开其作用域时(如main函数结束或exit语句),调用全局对象的析构函数。
4.new运算符动态建立对象:用delete运算符释放时,调用析构函数。
调用构造函数和析构函数的顺序:先构造的后析构。
建立对象数组:每一个数组元素都是同类的对象。
用指定参数的构造函数初始化数组,实例:
#include <iostream>
#include <string>
using namespace std;
class student{
private:int num;char name[10];char sex;
public:student(int n,string nam,char s)
{
num=n;
strcpy(name,nam);
sex=s;
cout<<"Constructor called."<<endl;
}
void student()
{cout<<"Constructor called."<<endl;}
void display();
};
void student::display()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
if(sex==0) {cout<<"男"<<endl;}
else {cout<<"女"<<endl;}
}
int main()//用指定参数的构造函数初始化数组
{
student stud[3]={student(1001,"张三",1),student(1002,"李四",0),student(1003,"王五",0)}//等号+大括号+类名+小括号+数据(逗号隔开)
cout<<"学生一:";stud[0].display();
cout<<"学生二:";stud[1].display();
cout<<"学生三:";stud[2].display();
return 0;
}
对象指针
对象空间的起始地址就是对象的指针。
可以定义一个指针,用来存放对象的地址。
指向对象的指针,实例1:
定义格式:1.类名 *指针变量名 2.指针变量=&对象名
class Time{
public:int hour,minute,sec;
void put()
{hour=12;minute=0;sec=0;}
};
int main()
{
Time *pt,t1;//类名 *指针变量名
pt=&t1;
pt->put();///等价(*pt).put()
cout<<pt->hour<<":"<<pt->minute<<":"<<pt->sec<<endl;//方式一
cout<<(*pt).hour<<":"<(*pt).minute<<":"<<(*pt).sec<<endl;//方式二
return 0;
}
指向对象数据成员的指针,实例2:
定义格式:数据类型名 *指针变量名
int *p1;//定义指向整型数据的指针变量
pl=&t1.hour;//将t1的数据成员hour地址赋给指针p1
cout<<*pl<<endl;//输出t1.hour的值
指向对象成员函数的指针,实例3:
定义格式:1.返回数据类型(类名::*指针变量名)(参数列表) 2.指针变量名=&类名::成员函数名
void (*p)();//p是指向void类型函数的指针变量
p=fun;//将fun函数的入口地址赋给指针变量p
(*p)();//调用定义
void(Time::*p2)();//"()"必须加上
p2=&Time::put;
对象指针综合应用,实例4:
#include <iostream>
using namespace std;
class Time{
public:int hour,minute,sec;
Time(int h,int m,int s)
{
hour=h,minute=m,sec=s;
}
void get_Time()
{
cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
};
int main()
{
Time t1(10,12,56);
int *pl=&t1.hour;//定义指向整型数据的指针,在定义的同时初始化
cout<<*p1<<endl;
t1.get_Time();//调用t1的成员函数
Time *p2=&t1;//定义指向Time对象的指针变量p2,并指向t1
p2->get_Time();//调用p2所指向对象的成员函数
void(Time::p3)();定义指向Time类公有成员函数的指针变量p3
p3=&Time::get_Time;//使p3指向Time类公有成员函数get_Time()
(t1.*p3)();//调用p3所指的成员函数t1.get_Time()
return 0;
}
指向当前对象的指针:this,指向本对象的指针,值是当前被调用成员函数所在对象的起始地址。
共用数据的保护(const)
const类型:像常量一样不能被改变,不能改变别人。
常对象
类名 const 对象名(参数列表);
const 类名 对象名(参数列表);//两者等价
Time const t1(12,34,56);//对象t1的所有数据成员都被保护,不能修改
private类型:数据不能共享,不能篡改。
const类型:数据能共享,不能篡改。
常对象:只能调用该对象的const类型成员函数(系统隐含调用构造和析构函数除外)。
const成员函数(常成员函数):只引用不改变数据成员。
例:
函数返回类型 函数名(参数列表) const
void get_Time const;//将函数声明成const类
常对象成员
1.常数据成员
常数据成员:用const声明常数据成员,如const int hour。
初始化:不能采用在构造函数中对常数据成员赋予初值的方法,只能通过构造函数的参数初始化表对常数据成员进行初始化。
值不可更改:一个确定的对象中该数据成员的值不可改变。
2.常成员函数
常成员函数:用来引用const数据成员,只能引用,不能修改,如:
void get_Time() const;//const在最后
const是函数类型的一部分,在声明和定义函数时都要使用。
const成员函数:不能调用另一个非const成员函数,不能改变数据成员。
什么情况使用常成员函数?
1.一些数据成员需要保护:将需要保护的数据成员声明为const。
2.所有数据成员需要保护:把所有数据成员声明为const。
例:
class Student(){
public:
const int num;//此数据成员不允许被改变
char name;
void display() const;//此函数用来使用const类型的数据成员num
void set();//此函数不能使用const数据成员num
};(display函数体省略)
#include <iostream>
using namespace std;
int main(){
const Student s1;//s1成员num,name不允许改变,只能用const函数display
Student s2;//s2的const成员num不允许改变,其他成员name允许改变
return 0;
}
指向对象的常指针
例:
Time *const ptr=&t1;
指向常对象的指针变量
例:
const Time *p=&t1;
对象的常引用
引用:用于函数形参,带回改变后的变量值。
对象的常引用:不希望函数中修改参数,把引用型形参定义成const,函数中不能改变形参值,也不能改变对应的实参值。
例:
函数返回类型 函数名(const 形参类型 &形参名)
什么时候使用常指针和常引用? 1.作为函数参数。2.能保证数据安全,不被修改。3.调用函数时不必建立实参的拷贝,提高运行效率、节省内存空间。
共用数据的保护(const)总结:
常对象:Time const t1或const Time t1
常对象成员:常数据成员(const int hour)、常成员函数(void get Time() const)
指向对象的常指针:Time *const ptr=&t1
指向常对象的指针变量:const Time *p=&t1
对象的常引用:函数返回类型 函数名(const 形参类型 &形参名)
不同对象间的数据共享(static)
C++中使用静态数据成员实现在多个函数中共享一个变量值。
C语言中使用全局变量,但随时可以被改变,安全得不到保障。
静态数据成员:使用关键字static定义,其值在各个对象中相同,改变其值,则各个对象中同时改变。静态数据成员不属于任何对象,属于类,只占一份内存空间,分配对象空间时,不分配静态数据成员空间。
静态数据成员的初始化:只能在类体外初始化,不能用参数初始化表初始化静态数据成员。
静态数据成员初始化格式:数据类型 类名::静态数据成员名=初值。
静态数据成员的引用:可通过类名引用,也可通过对象名引用。
例:
#include <iostream>
using namespace std;
class Box{
public:
Box(int,int);
int volume();
static int height;
int width;
int length;
};
Box::Box(int w,int l)
{
width=w;
length=l;//初始化两个数据成员
}
int Box::volume()
{
return height*width*length;
}
int Box::height=10;//静态数据成员类体外单独进行初始化
int main
{
Box a(15,20),b(20,30);
cout<<a.height<<endl;
cout<<b.height<<endl;//用对象名访问静态数据成员(不管是访问a还是访问b,高度固定为10)
cout<<Box::height<<endl;//用类名访问静态数据成员(仍然是10)
cout<<a.volume()<<endl;//求a的体积
return 0;
}
静态成员函数:在成员函数前加关键词static,就变成静态成员函数。静态成员函数可直接引用本类的静态数据成员。它不属于任何一个对象,没有this指针,因此静态成员函数不能访问本类中的非静态数据成员,除非使用“对象名.非静态数据成员”的形式。尽量只用静态成员函数引用静态数据成员,而不引用非静态数据成员。
例:
#include <iostream>
using namespace std;
class Student{
public:
Student(int,int,int);
void total();
static float average();
private:
int num;
int average;
static float sum;
static float count;
}
Student::Student(int m,int a,int s)
{
num=m;
age=a;
score=s;
}
void Student::total()
{
sum+=score;
count++;
}
float Student::average()
{
return(sum/count);
}
float Student::sum=0;
int Student::count=0;//静态数据成员类体外单独进行初始化
int main()//求n个学生平均成绩
{
Student stud[3]={Student(1001,18,70),Student(1002,19,79),Student(1005,20,98),}
int n;
cout<<"Please input the number of students:";
cin>>n;
for(int i=0;i<n;i++)
stud[i].total();
cout<<"The average score of"<<n<<"students is"<<stud[0].average()<<endl;//不管用stud[0]还是stud[1]还是类来调用都一样
return 0;
}
公有成员函数可以引用静态和非静态数据成员,静态成员函数一般只引用静态数据成员。
访问私有数据(friend)
访问类的私有成员:必须通过调用类的成员函数,但频繁调用成员函数影响程序运行效率。
友元:可以直接访问类的私有成员,提高程序运行效率,但友元机制在数据封装上开了孔,使用需要慎重。
友元函数的特点:直接访问类的私有成员。在类体内声明:在函数类型符前加关键字friend。在类体外定义:定义格式与普通函数相同。是非成员函数时,在调用上与普通函数相同。
例:
#include <iostream>
using namespace std;
class Time{
public:
Time(int,int,int);
friend void display(Time&);//声明
private:
int hour;
int minute;
int sec;
};
Time::Time(int h,int m,int s)
{
hour=h;
minute=m;
sec=s;
}
void display(Time &t)//定义
{
cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl;
}
int main()//输出时分秒
{
Time t1(10,13,56);
display(t1);
return 0;
}
当说明一个类为另一个类的友元时,友元类中的所有成员函数都是另一个类的友元函数。
友元是单向的,不能传递。