第26节 对象的定义和使用
1.对象的的定义及 对象的动态建立和释放
1、对象的的定义
►定义一个类时,也就是定义了一个具体的数据类型。若要使用类,需要将类实例化,即定义该类
的对象。
►需要注意,我们之前也使用了“对象”一词,那里主要是指数据对象。
►从现在起,“对象”一词专门表示类的实体。
►1.先定义类类型再定义对象
►有两种定义对象的形式:
►①将类的名字直接用作类型名:
类名 对象名列表;
►②指定关键字class或struct,后面跟着类的名字:
class 类名 对象名列表;
或
struct 类名 对象名列表;
►对象名列表是一个或多个对象的序列,各对象之间用逗号(,)分隔,最后必须用分号(;)结
束,对象取名必须遵循标识符的命名规则。例如:
Point a,b; //C++特色定义对象
class Point x,y; //兼容C语言特色定义对象
►2.定义类类型的同时定义对象
►一般形式为:
class 类名 { //类体
成员列表
} 对象名列表;
►例如:
class Point { //类体
public: …//公有的数据成员和成员函数
private: …//私有的数据成员和成员函数
} one , two; //对象列表
►3.直接定义对象
►一般形式为:
class { //类体
成员列表
} 对象名列表;
►例如:
class { //无类名类体
public: …//公有的数据成员和成员函数
private: …//私有的数据成员和成员函数
} p1 , p2; //对象列表
►一般而言,定义类型时不进行存储空间的分配 。
►当定义一个对象时,将为其分配存储空间 。
►有时人们希望在需要用到对象时才创建(create)对象,在不需要用该对象时就撤销(destroy)
它,释放其所占的存储空间,从而提高存储空间的利用率。
►利用new运算符可以动态地分配对象空间,delete运算符释放对象空间。
2、对象的动态建立和释放
►动态分配对象的一般形式为:
类名 * 对象指针变量;
对象指针变量 = new 类名;
►例如:
Point *p; //定义指向Point对象的指针变量
p = new Point; //动态分配Point对象
►用new运算动态分配得到的对象是无名的,它返回一个指向新对象的指针的值,即分配得到是
对象的内存单元的起始地址。程序通过这个地址可以间接访问这个对象,因此需要定义一个指
向类的对象的指针变量来存放该地址。显然,用new建立的动态对象是通过指 针来引用的。
►在执行new运算时,如果内存不足,无法开辟所需的内存空间, C++编译器会返回一个0值指
针。因此,只要检测返回值是否为0,就可以判断动态分配对象是否成功,只有指针有效时才
能使用对象指针。
►当不再需要使用由new建立的动态对象时,必须用delete运算予以撤销。例如:
delete p; //撤销p所指向的Point对象
►释放了p所指向的对象。此后程序不能再使用该对象。
►请注意,new建立的动态对象不会自动被撤销,即使程序运行结束也是如此,必须人为使
用delete撤销。
【例26.1】
1 #include<iostream>
2 using namespace std;
3 class Box
4 { public:
5 int width, length, height;
6 };
7 int main()
8 {
9 Box * p=new Box;
10 p->width=10; p->length=20; p->height=30;
11 cout<<p->width<<"\t"<<p->length<<"\t"<<p->height<<endl;
12 delete p;
13 return 0;
14 }
2.对象成员的引用、对象的赋值、对象的指针
3、对象成员的引用
►访问对象中的成员可以有3种方法:
►①通过对象名和对象成员引用运算符(.)访问对象中的成员;
►②通过指向对象的指针和指针成员引用运算符(->)访问对象中的成员;
►③通过对象的引用变量和对象成员引用运算符(.)访问对象中的成员;
►1.通过对象名访问对象中的成员
►访问对象中数据成员的一般形式为:对象名.成员名
►调用对象中成员函数的一般形式为:对象名.成员函数(实参列表)
►需要注意,从类外部只能访问类公有的成员。
►例如已定义一个类:
class Data { //Data类
public:
int data;
void fun(int a,int b,int d);
private:
void add(int m) { data+=m; }
int x,y;
……
};
►则函数
void caller1()
{
Data A, B; //定义对象
A.fun(1,2,3); //正确,类外部可以访问类的public成员函数
A.data=100; //正确,类外部可以访问类的public数据成员
A.add(5); //错误,类外部不能访问类任何private的成员
A.x=A.y=1; //错误,类外部不能访问类任何private的成员
B.data=101; //正确,A.data和B.data是两个对象的数据成员,为不同的存储空间
B.fun(4,5,6); //正确,A.fun和B.fun函数调用相同的代码,但作用不同的数据成员
}
►2.通过对象指针访问对象中的成员
►访问对象中数据成员的一般形式为: 对象指针—>成员名
►调用对象中成员函数的一般形式为: 对象指针—>成员函数(实参列表)
►则函数
void caller1()
{
Data A, &r=A; //定义对象引用变量
r.data=100; //正确,类外部可以访问类的public数据成员
r.fun(1,2,3); //正确,类外部可以访问类的public成员函数
}
4、对象的赋值
►如果一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值。这里所指的对象的“值”
是指对象中所有数据成员的值。
►对象赋值的一般形式为: 对象名1=对象名2
►注意对象名1和对象名2必须属于同一个类。
►关于对象赋值的说明:
►(1)对象的赋值只对其中的非静态数据成员赋值,而不对成员函数赋值。
►(2)如果对象的数据成员中包括动态分配资源的指针,按上述赋值的原理,赋值时只复制
了指针值而没有复制指针所指向的内容。
5、对象、对象指针或对象引用作为函数的参数和返回值
►函数的参数可以是对象、对象指针或对象引用。
►(1)当形参是对象时,实参要求是相同类的对象名,C++不能对类对象进行任何隐式类型
转换。此时形参是实参对象的副本。
►采用这样的值传递方式会增加函数调用在空间、时间上的开销,特别是当数据成员的
长度很大时,开销会急剧增加。
►实际编程中,传递对象时需要考虑类的规模带来的调用开销,如果开销很大时建议不
用对象作为函数参数。
►(2)当形参是对象指针时,实参要求是同类对象的指针,C++不 能对对象指针进行任何
隐式类型转换。
►函数调用时,无论类多大规模,传递的参数是一个地址值,其开销非常小。
►采用地址传递方式,在函数中若按间接引用方式修改了形参对象本质上就是修改实
参对象。因此,使用对象指针作为函数参数可以向主调函数传回变化后的对象。
►(3)当形参是对象引用时,实参要求是同类的对象,其功能与对象指针相似。
►此时函数形参对象实际上是实参对象的别名,为同一个对象。在函数中若修改了形参
对象本质上就是修改实参对象。因此,使用对象引用作为函数参数可以向主调函数传回
变化后的对象。
【例26.2】
1 #include <iostream>
2 using namespace std;
3 void func1(Data a,Data *p,Data &r)
4 {
5 a.data=100; p->data=200; r.data=300;
6 }
7 int main()
8 {
9 Data A, B, C;
10 A.fun(1,2,3); B.fun(4,5,6); C.fun(7,8,9);
11 func1(A,&B,C);//将对象A、B的地址、对象C的引用传递到函数func1
12 return 0;
13 }
►如果不希望在函数中修改实参对象的值,函数形参可以作const限定,例如:
void func2(Data a,const Data *p,const Data &r)
{
a.data=100;
p->data=200; //错误,左值是const对象
r.data=300; //错误,左值是const对象
}
►不必对对象形参作const限定,如上述代码中的形参a,因为即使在函数中修改了a,也不会影响
实参对象。
►函数返回值可以是对象、对象指针或对象引用。
►函数返回对象时,将其内存单元的所有内容复制到一个临时对象中。因此函数返回对象时会
增加调用开销。
►函数返回对象指针或引用,本质上返回的是对象的地址而不是它的存储内容,因此不要返回
局部对象的指针或引用,因为它在函数返回后是无效的。
►例:
Data func1()
{ Data a; a.fun(1,2,3);
return a; //可以返回局部对象,它被复制返回
}
Data* func2(Data *p1,Data *p2)
{ if (p1->data > p2->data) return p1;
return p2;
}
Data& func3(Data &r1,Data &r2)
{ if (r1.data > r2.data) return r1;
return r2;
}
void caller()
{
Data A, B, C;
A.fun(1,2,3);
B.fun(4,5,6);
C=func1();
func2(&A,&B)->data=100; //等价于 (&B)->data=100;
func3(A,B).data=100; //等价于 B.data=100;
}
►例:
Data* func()
{
Data a;
Data *p=&a;
return p; //返回局部对象a的指针p
}
void caller()
{
Data *p1;
p1=func();
p1->data=100; //a已不存在,引用错误
}