欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++及数据结构复习笔记(六)(类和对象2)

程序员文章站 2024-01-17 18:47:16
...

1.9 关于类和对象的进一步讨论

1.9.1 构造函数

       类的数据成员是不能再声明类时初始化的。

       如果一个类的所有数据成员是公用的,则可以在定义对象时对数据成员进行初始化,也可以用成员函数来对对象中的数据成员赋初值(如例1.8.1)。

       C++使用构造函数来处理对象的初始化,构造函数必须与类同名

       构造函数在类对象进入其作用域时调用,且构造函数没有返回值(不能写成void+构造函数名),也不能被调用。构造函数仅是对当前对象中的数据成员赋值,不是在声明类时直接对数据成员赋值。即程序首先建立对象,然后对对象中的成员赋初值。

类外声明构造函数:

       类名::构造函数名(类型1 形参1,类型2 形参2)

例 1.9.1使用构造函数对1.8.4小节右侧的程序进行改造。

#include<iostream>
using namespace std;
class Time
{
  public:
    Time();//声明构造函数
void set_time();//声明成员函数
void show_time();
  private:
int hour;
int minute;
int sec;
};
Time::Time()//定义构造函数
{
  hour=0;
  minute=0;
  sec=0;
}
int main()
{
  Time t1,t2;
  t1.set_time();
//调用成员函数,向t1的成员输入数据
t1.show_time();
//调用成员函数,输出t1的数据
t2.set_time();
t2.show_time();
return 0;
}
void Time::set_time()
//类外定义函数要指定作用域
{
  cin>>hour>>minute>>sec;
}
void Time::show_time()
{
cout<<hour<<”:”<<minute<<”:”<<sec<<endl;
}

1.9.2 带参数的构造函数

       带参数的构造函数的作用:对不同的对象赋不同的初值。

       构造函数首部的一般格式为:

       类中声明:            构造函数名(类型1 形参1,类型2 形参2,…);

       类外定义:            类名::构造函数名(类型1 形参1,类型2 形参2,…)

       在main中定义对象     类名 对象名(实参1,实参2,…);

例 1.9.2:有两个长方体,长宽高分别为:12x20x25,10x14x20,求它们的体积。编写一个基于对象的程序,在类中使用带参数的构造函数。

#include<iostream>
using namespace std;
class Box
{
  public:
Box(int,int,int);//声明带参数的构造函数
int volume();
  private:
int height;
int width;
int length;
};
Box::Box(int h,int w,int l)//在类外定义带参数的构造函数
{
  height=h;
  width=w;
  length=l;
}
void Box::volume()//在类外定义成员函数
{
  return(height*width*length);
}
int main()
{
  Box box1(25,20,12);//建立对象,并初始化长宽高
  cout<<”The volume of box1 is “<<box1.volume()<<endl;
  Box box2(20,14,10);
  cout<<”The volume of box2 is “<<box2.volume()<<endl;
  return 0;
}

       在一般的程序设计中,构造函数的声明采用参数初始化表更为简练。上例中的定义构造函数部分可以改造如下:

       Box::Box(inth,int w,int l):height(h),width(w),length(l) {}

       或者在构造函数声明时直接写:

       Box(inth,int w,int l):height(h),width(w),length(l) {}

1.9.3 使用默认参数的构造函数

       如果使用默认参数,则在建立对象时可以提供多种选择,其作用相当于好几个重载的构造函数。一个类只能有一个默认构造函数。且应在声明构造函数时就指定默认值。在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。

使用默认参数的构造函数的格式:

在类中

class 类名

{

  构造函数名(int x=1,int y=1,intz=1);//声明带默认参数的构造函数

};

类外

类名::构造函数名(int x,int y,int z):数据成员1(x),数据成员2(y),数据成员3(z) {} //类外定义

例 1.9.3将例1.9.2中的构造函数改为含默认参数的构造函数,长宽高均默认为10。

#include<iostream>
using namespace std;
class Box
{
public:
Box(int h=10,int w=10,int l=10);//在声明构造函数时指定默认参数
int volume();
  private:
int height;
int width;
int length;
};
Box::Box(int h,int w,int l):height(h),width(w),length(l) {} //在类外定义带参数的构造函数
void Box::volume()//在类外定义成员函数
{
  return(height*width*length);
}
int main()
{
  Box box1;//没给实参
  Box box2(15);//只给定一个实参
  Box box3(15,30); //只给定2个实参
  Box box4(15,30,20);//给定3个实参
  return 0;
}

1.9.4 析构函数

       析构函数的作用与构造函数相反,格式为:~类名。当对象的生命周期结束时,会自动执行析构函数。析构函数的作用并不是删除对象,而是在撤销对象所占用的内存之前完成一些清理工作。一个类可以有多个构造函数,但只能有一个析构函数。且析构函数不返回任何值,没有函数类型。一般是在声明类的同时调用析构函数,以指定如何完成清理工作。

调用构造函数和析构函数的顺序可参照图1.4。

C++及数据结构复习笔记(六)(类和对象2)

图 1.4 构造函数和析构函数的调用顺序

       关于它们的调用顺序,可以简单记为:先构造的后析构,后构造的先析构。它相当于一个栈,先进后出。

1.9.5 对象数组

       定义:由对象组成的数组,即数组的每个元素都是同类的对象。

Eg:

class Student
{
  public:
    Student(int n,int a,float s):num(n),age(a),float(s){}//定义构造函数
  private:
    int num;
int age;
float score;
};
Student stud[50];//定义对象数组stud,有50个元素
Student stud[3]={Student(1001,18,87),Student(1002,19,76),Student(1003,18,72)};
//建立对象数组时,分别调用构造函数,对每个元素进行初始化

1.9.6 对象指针

一、指向对象数据成员的指针和指向对象的指针

       对象空间的起始地址就是对象的指针。定义指向对象的指针变量的一般形式为:

       类名 *对象指针名;

       指向对象数据成员的指针变量的一般形式为:

       数据类型名 *指针变量名;

class Time
{
  public:
int hour;
int minute;
int sec;
    void get_time();
|;
void Time::get_time()
{cout<<hour<<”:”<<minute<<”:”<<sec<<endl;}
int main()
{
  Time *p;//定义p为指向Time类对象的指针变量
  int *p1;//定义指向对象整形数据的指针变量
  Time t1;
  p=&t1;//把t1的起始地址赋值给p
  p1=&t1.hour;//将对象t1的数据成员hour的地址赋值给p1
  (*p).hour;//p指向对象中的hour成员
  (*p).get_time();//调用p所指向的对象中的get_time函数
}

二、指向对象成员函数的指针

指向普通函数的指针

指向对象成员函数的指针

数据类型名 (*指针变量名)(参数列表)

数据类型名 (类名::*指针变量名)(参数列表)

指针变量名=&类名::成员函数名

void (*p)(int,int,int);

//p是指向void型函数的指针变量

p=fun;//将fun的入口地址赋给p

(*p)();//调用fun函数

void (Time::*p)();

//定义p为指向Time类*用成员函数的指针变量

p=&Time::get_time;//注意不加括号

1.9.7 this指针

       每个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为this。它是指向本类成员对象的指针,它的值是当前被调用的成员函数所在对象的起始地址。

1.9.8 常对象

       要使数据在一定范围内共享且不能被任意修改,可以使用const,即把有关的数据定义为常量。

       常对象必须要有初值。且如果一个对象被声明为常对象,则不能调用该对象的非const型成员函数。

格式:

       类名 const 对象名(实参列表)

       Time const t1(12,34,45);//t1是常对象

       t1.get_time();//错误,企图调用常对象的非const型成员函数

       如果想引用常对象的成员函数,需将成员函数声明为const。常成员函数可以访问常对象中的数据成员,但不允许修改它们。

       void get_time() const;//将成员函数声明为const

常数据成员

const int hour;//声明hour为常数据成员

Time::Time (int h):hour(h){}//使用参数初始化表对常数据成员hour进行初始化

常数据成员值无法修改,其初始化操作仅能采用构造函数的初始化表方式

常函数成员

void get_time() const;

常成员函数可以引用本类中的数据成员,但不能修改他们。

1.9.9 指向对象的常指针

       将指针变量声明为const型,这样指针值始终保持为初值,不能改变。其作用是防止误操作,增加安全性。

       定义指向对象的常指针的一般形式:

       类名 * const 指针变量名

       Time t1,t2;

       Time * const pt1;

       pt1=&t1;//pt1指向对象t1,此后无法改变指向

       pt1=&t2;//错误,pt无法改变指向

1.9.10 指向常对象的指针变量

指向常变量的指针变量

指向常对象的指针变量

const char *p;

const 类型名 *指针变量名

Time * const p;//指向对象的常指针变量

const Time *p;//指向常对象的指针变量

如果一个变量被声明为常变量,则只能用指向常变量的指针指向它。

如果一个对象被声明为常对象,则只能用指向常对象的指针指向它。如果定义一个指向常对象的指针变量并使其指向一个非const的对象,则其指向的对象无法通过指针来改变。

const型数据的小结

形式

含义

Time const t1;

t1是常对象,声明常对象t1

void Time::fun() const;

声明常成员函数fun

Time * const p;

p是指向Time类对象的常指针

const Time *p;

p是指向Time类常对象的指针变量

Time &t1=t

t1是Time类对象的引用

1.9.11 对象的动态建立和释放

       可以用new运算符动态地建立对象,用delete运算符撤销对象。用new运算符动态地分配内存后,将返回一个指向新对象的指针。

       Box *p;//定义一个指向Box类对象的指针变量

       p= new Box;//在p中存放新建对象的起始地址

       Box *p=new Box(12,15,18);//对新建立的对象进行初始化

       用new建立的对象一般通过指针访问。只要检测返回值是否为0,就可以判断分配内存是否成功。

1.9.12 对象的赋值与复制

对象的赋值

对象的复制

一个对象的值可以赋给同类的另一个对象,通过”=”实现。且对象的赋值仅对其中的数据成员赋值,不对成员函数赋值。

用一个已有的对象快速复制出多个完全相同的对象。

对象名1=对象名2;

Student stud1,stud2;//定义2个同类对象

stud2=stud1;

类名 对象2(对象1);

Box box2(box1);

或者:

类名 对象2=对象1;

Box box2=box1;

       注意:对象的赋值是对一个已经存在的对象赋值,因此必须先定义被赋值的对象,才能进行赋值。而对象的复制则是从无到有地建立一个新对象,并使它与一个已有的对象完全相同。

1.9.13 静态成员

       实现对象中的一个或几个数据成员为所有对象所共有,实现数据共享。

一、静态数据成员

class Box
{
  public:
int volume();
  private:
static int height;//把height定义为静态数据成员
int width;
int length;
};

       每个对象都可以引用静态数据成员,静态数据成员的值对所有对象都是一样的,静态数据成员在内存中只占一份空间。静态数据成员只能在类外进行初始化

       数据类型 类名::静态数据成员名=初值;

int Box::height=10;
Box(int h,int w,int l):height(h),width(w),length(l){}
//错误,height是静态成员,不能用参数初始化表进行初始化。

二、静态成员函数

       上例中,把volume函数声明一行改为:

       static int volume();

       就将其变为了静态成员函数。

       调用时需要加限定符::

       类名::函数名;

       静态成员函数的作用是为了能够处理静态数据成员,且静态成员函数无this指针,这决定了静态成员函数不能访问本类中的非静态成员。静态成员函数只能引用本类中的静态数据成员。
例 1.9.4 静态成员函数的简单应用
#include<iostream>
using namespace std;
class Student
{
  public:
Student(int n,int a,float s):num(n),age(a),score(s){}//参数初始化表定义构造函数
void total();
static float average();//声明静态成员函数
  private:
int num;
int age;
float score;
static float sum;//声明静态数据成员
static int count;
};
void Student::total()//定义非静态成员函数
{
  sum=sum+score;
  count++;
}
float Student::average()//定义静态成员函数
{
  return(sum/count);
}
float Student::sum=0;//对静态数据成员进行初始化
int Student::count=0;
int main()
{
  Student stud[3]={Student(1001,18,70),Student(1002,19,78),Student(1005,20,98)};
  //定义对象数组并初始化
  int n;
  cout<<”please input the number of students:”;
  cin>>n;//n表示需要求前面n的学生的平均成绩
  for (int i=0;i<n;i++)
stud[i].total();
  cout<<”the average score of “<<n<<” students is “<<Student::average()<<endl;
  //调用静态成员函数
  return 0;
}

1.9.14 友元

       友元可以访问与其有友好关系的类中的私有成员,其包括友元函数和友元类。

一、友元函数

       在本类以外的其他地方定义了一个函数,这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数,在本类类体中用friend对其进行声明,此函数就称为本类的友元函数。友元函数可以访问这个类的私有成员

将普通函数声明为友元函数

(不在任何类中的普通函数)

友元成员函数

(另一个类中的成员函数)

Eg:

#include<iostream>
using namespace std;
class Time
{
  public:
Time(int h,int m,int s): hour(h),minute(m),sec(s){}
friend void display();
//将display函数声明为Time类的友元函数
  private:
int hour;
int minute;
int sec;
};
void display(Time &t)//定义友元函数,形参t是Time类对象的引用
{
 cout<<t.hour<<”:”<<t,minute<<”:”<<t.sec<<endl;
}
int main()
{
  Time t1(10,13,56);
  display(t1);
  return 0;
}

下例中还包含了类的提前引用声明

#include<iostream>
using namespace std;
class Data;//对Data类的提前引用声明
class Time
{
  public:
Time(int h,int m,int s): hour(h),minute(m),sec(s){}
void display(Data &);//形参是Data类对象的引用
  private:
int hour;
int minute;
int sec;
};
class Data //声明Data类
{
  public:
Data(int m,int d,int y):month(m),day(d),year(y){}
friend void Time::display(Data &);
//声明Time类中的display函数为友元函数
  private:
int month;
int day;
int year;
};
void Time::display(Data &d)
{
 cout<<d.month<<”/”<<d.day<<”/”<<d.year<<endl;
 cout<<hour<<”:”<<minute<<”:”<<sec<<endl;
}
int main()
{
  Time t1(10,13,56);
  Data d1(12,25,2004);
  t1.display(d1);
  return 0;
}

由于display函数不是Time类成员函数,不能默认引用Time类数据成员,因此必须指定要访问的对象。

 

二、友元类

       把B类声明为A类的朋友,这是B类就是A类的友元类,B类中的所有函数都是A类的友元函数,可以访问A类的所有成员。

class A

{

  friendB;

};

class B

{};

相关标签: C++ C++数据结构