今天跟大家分享的小技巧是关于类和对象的。但准确的来说,这不是一个所谓的“技巧”,我更喜欢叫它“原理”。也就是说今天我们将一起学习类与对象的本质究竟是什么。希望这一篇关于类和对象的文章能够让更多的朋友对面向对象的程序设计有更深入的理解。
一、内存存储原理
我认为类与对象这两者之间并没有本质上的区别,类决定了对象,对象来源于类,类与对象都不能独立存在。当我们说起类时,就必须涉及其对象;而谈起对象时,又必须研究它所在的类。所以说类与对象一定要一同时存在,同时分析和使用。我们抛开它们在书本上的定义和概念,直接来看看它们的本质到底是什么,我们定义了一个类的对象,计算机究竟是如何来处理它们的。先来看一个简单的例子:
class Poultry
{
int age;
char name[10];
};
这是我们定义的一个类,它有两个属性,一个是年龄,一个是名字。接下来我们来定义这个类的对象:
//定义对象
Poultry p;
//定义一个对象指针
Poultry *p = new Poultry();
这是C++的写法,如果我们用Java来定义这个类的对象,则代码是这样的:
Poultry p = new Poultry();
其实,在Java里定义的对象就是C++里定义的对象指针。
我们先来看看定义对象和定义对象指针有什么区别,假设我们在一个函数内定义了这样的几个变量:
void myfunc()
{
int a;
int b;
Poultry p0;
Poultry *p1 = new Poultry();
}
这样定义的4个变量a、b、p0和p1是存放在“函数栈帧”里的(如果对函数栈帧不了解的话请参见《函数栈帧》),它们在内存中的排列方式如下:
这样看起来这4个变量没有任何区别,但上图是为了方便我们对变量的存储有所了解,实际上这4个变量的存放内容是这样的:
注意,如果我们定义了一个类对象变量,这个变量本身在内存中就存放了这个对象的各个属性;而对于对象指针变量来说,这个变量本身是一个指针型变量,存储的内容是一个地址,而这个地址中所存放的内容才是这个对象具有的属性。而这个对像实际上是通过new Poultry()创建的,在执行new Poultry()时,计算机根据Poultry这个类在定义时的大小来创建一个连续的内存空间。而这个对象所在的内存则是在堆内存中。
使用new修饰符来创建的对象从本质上来说,就是根据这个类的大小来申请这样一块区域内存。如果我们使用sizeof()函数来分别对p0和p1的占用内存做计算,结果分别是20和4,因为p0是一个类的对象,它的属性占用了20个字节的内存,而p1则是一个指针,指针占用内存大小为4(在32位体系结构下指针占用4个字节,64位体系结构下指针占用8个字节)
二、内容比较与地址比较
在实际编程过程中我们常常会遇到两个对象的比较问题,很多教材中提到比较两个对象时都说明不能使用双等号直接比较,但很少有教材对这个问题做说明,为什么不能直接用双等号比较两个对象。我们先来看看两个对象指针的比较问题:
Poultry *p1 = new Poultry();
Poultry *p2 = new Poultry();
Poultry *p3 = p2;
我们定义了这样3个对象指针,关于对象指针的比较我们可以这样写:
if (p1 == p2)
{
//do something
}
if (p2 == p3)
{
//do something
}
这样对指针的比较是合法的,为什么呢?因为p1、p2和p3这3个变量都是指针类型的变量,它们的类型相同,占用内存大小也相同,存放的内容都是4字节的地址,所以对指针变量用双等号比较是可以的,在这里我们称“==”这样的比较运算为“值比较”。
因为p1和p2都是通过new修饰符创建了两个对象之后记录了这两个区域的内存地址,这两个区域的内存地址必然不同,很显然p1 == p2的结果为假;而对于p3来说,它并没有通过new修饰符创建一个新的对象,也没有创建新的内存区域,p3指针的值是跟p2指针的值一样。p2和p3这两个指针所存放的内存地址完全相同,所以p2 == p3的结果为真。也就是说对于使用“==”运算符的“值比较”运算,表达式所计算的是两个基本变量的数值,如果数值相同则结果为真;否则结果为假。当然,除了双等号“==”之外,对于大于号、小于号、不等号这些数值比较符号来说都是适用的。值得注意的地方是:对象指针的“值比较”只是比较两个对象的地址是否相同,而并不是比较这两个指针所指向对象的内容是否相同。
下面我们来看看对象的内容比较,对于基本数据类型来说,它们有一个非常统一的特点,就是一个变量只能存放一个数值,比如int、long、float、double等,所以对于相同类型的变量可以直接进行“值比较”。而对于类的对象来说一个对象中可以存放多个值,比如对于Poultry类的对象就可以有两个值,一个是年龄age,另一个是名字name。所以对象之间是不能通过“值比较”进行直接运算的。但C++中提供了一种比较对象内容的方法,叫作“运算符重载”,如果我们想要比较Poultry类的对象,在定义Poultry类时需要编写一个“==”比较运算符的方法:
class Poultry
{
public:
int age;
char name[10];
int operator ==(Poultry &p)
{
if (this->age != p.age)
{
return 0;
}
if (strcmp(this->name, p.name))
{
return 0;
}
return 1;
}
};
int main(void)
{
Poultry p1;
Poultry p2;
Poultry p3;
p1.age = 1;
strcpy(p1.name, "duck");
p2.age = 1;
strcpy(p2.name, "duck");
p3.age = 1;
strcpy(p3.name, "chick");
if (p1 == p2)
{
cout
}
else
{
cout
}
if (p2 == p3)
{
cout
}
else
{
cout
}
return 0;
}
输出比较结果如下:
p1 == p2
p2 != p3
同样的在Java中使用“=”号给对象赋值,只是使一个这个对象指针的地址与另外一个对象指针的地址相同,“==”比较两个对象也只是比较两个对象的地址,并不是内容比较。如果要使用内容比较则同样需要使用equal()方法或是Compare工具类,或自己实现比较的接口。
今天的小技巧你学会了吗?
关注编程外星人
我想加入交流群
领取专属 10元无门槛券
私享最新 技术干货