类和对象

Posted petewell

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类和对象相关的知识,希望对你有一定的参考价值。

  • 对象的自身引用是面向对象程序设计语言中特有的、十分重要的一种机制。在C++中,为这种机制设立了专门的表示:this指针变量。
  • 在类的每一个成员函数的形参表中都有一个隐含的指针变量this,该指针变量的类型就是成员函数所属类的类型。
  • 当程序中调用类的成员函数时,this指针变量被自动初始化为发出函数调用的对象的地址。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
class Sample

int x, y;
public:
Sample(int a=0, int b=0)
x=a; y=b;
void print()
cout<<x<<endl;
cout<<y<<endl;

;
int main()
Sample obj(5,10);
obj.print();
return 0;

上述程序可用this指针显示改写为完全等价的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
class Sample

int x, y;
public:
Sample(int a=0, int b=0)
this->x=a; //在此例中this=&obj
this->y=b;
void print()
cout<<this->x<<endl; //在此例中this=&obj
cout<<this->y<<endl;

;
int main()
Sample obj(5,10); //当程序中调用类的成员函数时,this指针变量被自动
obj.print(); //初始化为发出函数调用的对象的地址。
return 0;

那么何时使用this指针呢?
编写代码时主要有两种场合要求尽可能使用this指针

  • 一种是为了区分成员和非成员

例如:

1
2
3
4
void Sample::fun(int x)

this->x=x;

  • 一种使用this指针的应用是一个类的方法需要返回当前对象的引用。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Sample

private:
int x;
char* ptr;
public:
Sample & Set(int i, char *p);
//..
;
Sample& Sample::Set(int i, char *p); //方法Set返回当前对象的引用,*this就是当前对象。

x=i;
ptr=p;
return *this;

对象数组与对象指针

对象数组

所谓对象数组是指每一数组元素都是对象的数组,也就是说,若一个类有若干个对象,我们把这一系列的对象用一个数组来存放。
对象数组的元素是对象,不仅具有数据成员,而且还有函数成员。定义对象数组时,系统为每个数组元素对象调用一次构造函数以构造这些元素。
对象数组的定义的格式为:

1
类名 数组名 [数组大小];

例如:

1
2
3
Student stu[3];
定义了类Student的对象数组stu。
系统调用无参构造函数3次。

如果类Student有2个数据成员姓名(char name[10])、年龄(int age),那么在定义对象数组时也可以实现初始化。
例如:

1
Student stu[3]= Student(“zhao”,22),  Student(“qian”,20),  Student(“sun”,8,90)  ;

在建立对象数组时,分别调用构造函数,对每个元素初始化。
与基本数据类型的数组一样,在使用对象数组时也只能访问单个数组元素,也就是一个对象,通过这个对象,也可以访问到它的公有成员,一般形式是:

1
数组名[下标]. 成员名

对象数组的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;
class Sample

private:
int x;
public:
void Set_x(int n)
x=n;
int Get_x()
return x;
;

int main()

Sample obj[4];
int i;
for(i=0; i<4; i++)
obj[i].Set_x(i);
for(i=0; i<4; i++)
cout<<obj[i].Get_x()<<” ”;
cout<<endl;
return 0;

运行结果为:0 1 2 3

堆对象

使用new运算符动态分配的对象属于堆对象,其所占存储空间被分配在堆区。

利用new建立对象会自动调用构造函数,利用delete删除对象会自动调用析构函数。
堆对象的建立与删除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
class Date //定义日期类Date
private:
int month, day, year;
public:
Date(int m, int d, int y); //声明构造函数
;
Date::Date(int m, int d, int y)   //定义构造函数
if (m>0 && m<13)
month=m;
if (d>0 && d<32)
day=d;
if (y>0 && y<3000)
year=y;

int main()         
Date *pd; //定义一个指向Date类的对象的指针变量pd
pd=new Date(1,1,2008); //pd指向新建Date类的对象的起始地址
//……
delete(pd); //释放pd所指向的内存空间
return 0;

对象指针

指向类对象的指针称为对象指针。

声明指向类对象的指针变量的一般形式为
类名 *对象指针名;
用指针引用单个对象成员的方法与其他基本类型指针相同,可以有两种形式:
指针变量名->成员名

(*指针变量名).成员名
可以通过对象指针访问对象和对象的成员。

例如:

1
2
Date obj, *pt;
pt=&obj;

定义指向Date类对象obj的指针变量pt。

1
2
3
4
5
*pt             //pt所指向的对象,即obj
(*pt).year //pt所指向的对象obj中的year成员,即obj.year
pt->year //pt所指向的对象obj中的year成员,即obj.year
(*pt).get_date() //调用所指向的对象obj中的get_date函数,即obj.getdate()
pt->get_date //调用所指向的对象obj中的get_date函数,即obj.getdate()

有关对象指针的使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;
class Date //定义日期类Date
private:
int month, day, year;
public:
Date(int m, int d, int y); //声明构造函数
void showDate();     //声明显示数据成员值的成员函数
;
Date::Date(int m, int d, int y)   //定义构造函数
if (m>0 && m<13)
month=m;
if (d>0 && d<32)
day=d;
if (y>0 && y<3000)
year=y;

void Date::showDate()

cout<<year<<”.”<<month<<”.”<<day<<endl;


int main()

Date obj(2008,1,1); //定义Date类对象obj
int *pt1=&obj.year;
//定义指向整型数据的指针变量pt1,并使pt1 指向obj.year
cout<<*pt1<<endl; //输出pt1所指向的数据成员obj.year
obj.showDate(); //调用对象obj的成员函数showDate()
Date *pt2=&obj;
//定义指向Date类对象的指针变量pt2,并使pt2指向obj
pt2->showDate(); //调用pt2所指向对象obj的showDate()函数
return 0;

运行结果为:
2008
2008.1.1
2008.1.1

静态成员

为了实现一个类的不同对象之间的数据和函数共享,C++提出了静态成员的概念。静态成员包括:

  • 静态数据成员
  • 静态函数成员

在类的定义中,可以用关键字static声明成员为静态成员,这些静态成员可以在同一个类的不同对象之间提供数据共享。
不管这个类创建了多少个对象,但静态成员只有一份拷贝(副本),为所有属于这个类的对象所共享。

静态数据成员

公有的静态数据成员可以在对象定义之前被访问,形式为:
类名::公有静态成员变量名;
在对象定义后还可以通过对象进行访问,形式为:
对象名.公有静态成员变量名;
私有的静态数据成员不能被类的外部函数访问,也不能用对象进行访问。
静态数据成员的初始化必须在类外进行,默认值为0,它是在编译时创建并初始化,所以它在该类的任何对象被创建前就存在。

静态数据成员的定义与使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<iostream>
using namespace std;
class Ctype
private:
int a;
static int s; //定义私有的静态数据成员s
public:
void print();
Ctype(int x=0);
;
void Ctype::print()
cout<<”a=”<<++a<<endl; //输出普通数据成员
cout<<”s=”<<++s<<endl; //输出静态数据成员,两者作比较

Ctype::Ctype(int x)
a=x;

int Ctype::s=0; //静态数据成员赋初值在类体外进行,前面不能再加static
int main()
Ctype c1,c2,c3; //定义3个对象,都使用默认的参数值
c1.print();
c2.print();
c3.print();
return 0;

运行结果为:
a=1
s=1
a=1
s=2
a=1
s=3

静态数据成员和一般数据成员的不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
using namespace std;
class Student

private:
static int count; //声明静态数据成员count,统计学生的总数
int StudentNo; //普通数据成员,用于表示每个学生的学号
public:
Student() //构造函数

++count; //每创建一个学生对象,学生数加1
StudentNo=count; //给当前学生的学号赋值

void Print() //成员函数,显示学生的学号和当前学生数

cout<<”Student”<<StudentNo<<” ”;
cout<<”count=”<<count<<endl;

;

int Student::count=0; //给静态数据成员couny赋初值
int main()

Student Student1; //创建第1个学生对象
Student1.print();
cout<<"---------"<<endl;
Student Student2; //创建第2个学生对象 Student2
Student1.Print();
Student2.Print();
cout<<"----------"<<endl;
Student Student3; //创建第3个学生对象Student3
Student1.Print();
Student2.Print();
Student3.Print();
cout<<"----------"<<endl;;
Student Student4; //创建第4个学生对象Student4
Student1.Print();
Student2.Print();
Student3.Print();
Student4.Print();
return 0;

运行结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
Studentl count=1
----------
Studentl count=2
Student2 count=2
----------
Studentl count=3
Student2 count=3
Student3 count=3
----------
Studentl count=4
Student2 count=4
Student3 count=4
Student4 count=4

静态成员函数

在类定义中,声明为static的成员函数能在类的范围内共享,把这样的成员函数称为静态成员函数。
静态成员函数属于整个类,是该类所有对象共享的成员函数,而不属于类中的某个对象。
静态成员函数只能访问静态数据成员,不能对类的其他类型的数据成员或成员函数进行访问,可以通过对象或类名进行调用。

定义静态成员函数的格式为
static返回类型 静态成员函数名(参数表);
与静态数据成员类似,调用公有静态成员函数的一般格式为
类名::静态成员函数名(实参表)
对象.静态成员函数名(实参表)

关于静态成员函数的使用有以下几点说明:

  1. 静态成员函数可以定义成内嵌的,也可以在类外定义,在类外定义时,不用static前缀。
  2. 一般情况下,静态函数成员主要用来访问全局变量或同一个类中的静态数据成员。特别是,当它与静态数据成员一起使用时,达到了对同一个类中对象之间共享数据进行维护的目的。
  3. 私有静态成员函数不能被类外部函数和对象访问。
  4. 使用静态成员函数的一个原因是,可以用它在建立任何对象之前处理静态数据成员,这是普通成员函数不能实现的功能。
  5. 编译系统将静态成员函数限定为内部连接,也就是说,与现行文件相连接的其他文件中的同名函数不会与该函数发生冲突,维护了该函数使用的安全性,这是使用静态成员函数的另一个原因。
  6. 在一般的成员函数中都隐含有一个this指针,用来指向对象自身,而在静态成员函数中是没有this指针的。
  7. 一般而言,静态成员函数不访问类中的非静态成员。若确实需要,静态成员函数只能通过对象名(或指向对象的指针)访问该对象的非静态成员。

友元

类的一个很重要的特点就是实现了数据隐藏和封装。在类定义的时候,一般都将数据成员声明为私有成员(或保护成员),只能在类定义的范围内使用,也就是说私有成员(或保护成员)只能通过它的成员函数来访问。

但是,调用公有函数接口是需要时间开销的,且有时候需要在类的外部访问类的私有成员(或保护成员)。
为此,就需要寻找一种途径,在不放弃私有成员(或保护成员)数据安全性的情况下,使得一个普通函数或者类的成员函数可以访问到封装于某一类中的信息(私有、保护成员) 。

在C++语言中,通过定义友元(friend member)来实现这一功能。类的友元包括友元函数友元类

友元函数

友元函数有2种形式:

  • 一个不属于任何类的普通函数声明为当前类的友元,称为当前类的友元函数。
  • 一个其他类的成员函数声明为当前类的友元,称为当前类的友元成员。

友元函数是在类中说明的由关键字friend修饰的非成员函数。
友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有成员、保护成员和公有成员。
在类中声明友元函数时,需在其函数名前加上关键字friend。此声明可以放在公有部分,也可以放在保护部分和私有部分。
友元函数可以定义在类内部,也可以定义在类的外部。

友元函数的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream.h>
#include<cmath>
class Point

int x, y;
public:
Point(int a=0, int b=0)
x=a; y=b;
~Point()
void Show()
cout<<”x=”<<x<<” y=”<<y<<endl;
int Get_x()
return x;
int Get_y()
return y;
friend double Distance(Point &p1, Point &p2); //友元函数声明
;

double Distance(Point &p1, Point &p2) //友元函数定义

return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));

int main()
Point p1, p2(1,1);
cout<<Distance(p1,p2)<<endl;
return 0;

友元函数的说明:

  • 友元函数不是类的成员函数。因此,对友元函数指定访问权限无效,可以把友元函数的说明放在privatepublicprotected的任意段中。在类的外部定义友元函数时,不必像成员函数那样,在函数名前加上类名::
  • 使用友元函数的目的是提高程序的运行效率。
  • 慎用友元函数,因为它可在类外直接访问类的私有或保护成员,破坏了类的信息隐藏的特性。

友元成员

除了一般的函数可以作为某个类的友元外,一个类的成员函数也可以作为另一个类的友元。
这种成员函数不仅可以访问自己所在类对象中的所有成员,还可以访问friend声明语句所在类对象中的所有成员。
这样能使两个类相互合作、协调工作,完成某一任务。

将一个类的成员函数说明为另一个类的友元函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<iostream>
using namespace std;
class N; //N类的前向说明,因为N的定义在M之后,而M中使用了N
class M

int a, b;
public:
M(int x, int y)
a=x; b=y;
void Print()
cout<<”a=”<<a<<” b=”<<b<<endl;
void Setab(N &);
;
class N

int c, d;
public:
N(int a, int b)
c=a; d=b;
void Print()
cout<<”c=”<<c<<” d=”<<d<<endl;
friend void M::Setab(N&); //将M类的成员函数说明为本类的友元函数
;

void M::Setab(N &obj) //M类的成员函数Setab()是类N的友元函数

a=obj.c; //在Setab()中可以直接访问类N的私有成员
b=obj.d;

int main()
M m(25, 40);
N n(55,66);
cout<<”m: ”;
m.Print();
cout<<”n: ”;
n.Print();
m.Setab(n);
cout<<”m: ”;
m.Print();
return 0;

运行结果为:
m: a=25 b=40
n: c=55 d=66
m: a=55 b=66

友元类

友元还可以是类,即一个类可以作为另一个类的友元。
当一个类作为另一个类的友元时,则该类中的所有成员函数都是另一个类的友元成员,都可以访问另一个类的所有成员。
友元类的声明格式为:
friend 类名;
此语句可以放在公有部分也可以放在私有部分或保护部分。

将一个类说明为另一个类的友元类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<iostream>
using namespace std;
class B; // 前向说明,因为在后面定义的A类中用到B类
class A

int x;
public:
A(int a)
x=a;
friend class B; //将类B声明为类A的友元类
;

class B

public:
void Show(A a)
cout<<”x=”<<a.x<<endl;
;
int main()

A a(10);
B b;
b.Show(a);
return 0;

运行结果为:
x=10

说明:

  • 本例中,类B是类A的友元,因此,在类B的成员函数中可以访问类A的任何成员。
  • 友元关系是单向的,不具有交换性,即类A中将类B声明为自己的友元类,但类B中没有将类A声明为友元类,所以类A的成员函数不可以访问类B的私有成员。
  • 当两个类都将对方声明为自己的友元时,才可以实现互访。
  • 友元关系也不具备传递性,即类A将类B声明为友元,类B将类C声明为友元,此时,类C不一定是类A的友元。

原文:大专栏  类和对象(二)


以上是关于类和对象的主要内容,如果未能解决你的问题,请参考以下文章

JavaSE:类和对象

类和对象

Python的类和对象入门

类和对象

类和对象的使用

第1章 类和对象——定义类和创建对象