C++程序设计入门——类和对象的进阶

目录

C++类和对象的进阶

综述

类对象初始化

对象数组

对象指针

共用数据的保护(const)

不同对象间的数据共享(static)

访问私有数据(friend)


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;
}

当说明一个类为另一个类的友元时,友元类中的所有成员函数都是另一个类的友元函数。

友元是单向的,不能传递。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那我的单纯呢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值