指向对象的const指针
将指针变量声明为const型,这样指针值始终保持为其初值,不能改变。(与java的final关键字类似)
如:
Time t1(10,12,15),t2; //定义对象
Time * const ptr1; //const位置在指针变量名前面,规定ptr1的值是常值
ptr1=&t1; //ptr1指向对象t1,此后不能再改变指向
ptr1=&t2; //错误,ptr1不能改变指向
定义指向对象的常指针的一般形式为:
类名 * const 指针变量名;
也可以在定义指针变量时使之初始化,如将上面第2, 3行合并为:
Time * const ptr1=&t1; //指定ptr1指向t1
注:指向对象的常指针变量的值不能改变,即始终指向同一个对象,但可以改变其所指向对象(如t1)的值。
指向const对象的指针变量
1.指向const变量的指针变量
指向常变量的指针变量ptr:
const char *ptr;
定义指向常变量的指针变量的一般形式为:
const 类型名 *指针变量名;
注意const的位置在最左侧,它与类型名char紧连,表示指针变量ptr指向的char变量是常变量,不能通过ptr来改变其值的。
1) 如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般的(指向非const型变量的)指针变量去指向它。
2) 指向常变量的指针变量除了可以指向常变量外,还可以指向未被声明为const的变量。此时不能通过此指针变量改变该变量的值。
3) 如果函数的形参是指向非const型变量的指针,实参只能用指向非const变量的指针,而不能用指向const变量的指针,这样,在执行函数的过程中可以改变形参指针变量所指向的变量(也就是实参指针所指向的变量)的值。
2.指向const对象的指针变量
1) 如果一个对象已被声明为常对象,只能用指向常对象的指针变量指向它,而不能用一般的(指向非const型对象的)指针变量去指向它。
2) 如果定义了一个指向常对象的指针变量,并使它指向一个非const的对象,则其指向的对象是不能通过指针来改变的
Time t1(10,12,15); //定义Time类对象t1,它是非const型对象
const Time *p = &t1; //定义p是指向常对象的指针变量,并指向t1
t1.hour = 18; //合法,t1不是常变量
(* p).hour = 18; //非法,不能通过指针变量改变t1的值
3) 指向常对象的指针最常用于函数的形参,目的是在保护形参指针所指向的对象,使它在函数执行过程中不被修改。
请记住这样一条规则: 当希望在调用函数时对象的值不被修改,就应当把形参定义为指向常对象的指针变量,同时用对象的地址作实参(对象可以是const或非const型)。如果要求该对象不仅在调用函数过程中不被改变,而且要求它在程序执行过程中都不改变,则应把它定义为const型。
4) 如果定义了一个指向常对象的指针变量,是不能通过它改变所指向的对象的值的,但是指针变量本身的值是可以改变的。
对象的常引用
一个变量的引用就是变量的别名。实质上,变量名和引用名都指向同一段内存单元。如果形参为变量的引用名,实参为变量名,则在调用函数进行虚实结合时,并不是为形参另外开辟一个存储空间(常称为建立实参的一个拷贝), 而是把实参变量的地址传给形参(引用名),这样引用名也指向实参变量。
#include <iostream>
using namespace std;
class Time
{
public:
Time(int,int,int);
int hour;
int minute;
int sec;
};
Time::Time(int h,int m,int s) //定义构造函数
{
hour=h;
minute=m;
sec=s;
}
void fun(Time &t)
{
t.hour=18;
}
int main( )
{
Time t1(10,13,56);
fun(t1);
cout<<t1.hour<<endl;
return 0;
}
如果不希望在函数中修改实参t1的值,可以把引用变量t声明为const(常引用),函数原型为
void fun(const Time &t);
对象的动态建立和释放
如果已经定义了一个Box类,可以用下面的方法动态地建立一个对象:
new Box;
编译系统开辟了一段内存空间,并在此内存空间中存放一个Box类对象,同时调用该类的构造函数,以使该对象初始化,但是此时用户还无法访问这个对象,因为这个对象既没有对象名,用户也不知道它的地址。这种对象称为无名对象。
用new运算符动态地分配内存后,将返回一个指向新对象的指针的值,即所分配的内存空间的起始地址。用户可以获得这个地址,并通过这个地址来访问这个对象。需要定义一个指向本类的对象的指针变量来存放该地址。
Box *pt; //定义一个指向Box类对象的指针变量pt
pt=new Box; //在pt中存放了新建对象的起始地址
在不再需要使用由new建立的对象时,可以用delete运算符予以释放。如
delete pt; //释放pt指向的内存空间
这就撤销了pt指向的对象。此后程序不能再使用该对象。
如果用一个指针变量pt先后指向不同的动态对象,应注意指针变量的当前指向,以免删错了对象。在执行delete运算符时,在释放内存空间之前,自动调用析构函数,完成有关善后清理工作。
对象之间相互赋值
如果对一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值,或者说,一个对象的值可以赋给另一个同类的对象。这里所指的对象的值是指对象中所有数据成员的值。
对象赋值的一般形式为:
对象名1 = 对象名2;
注意对象名1和对象名2必须属于同一个类。例如
Student stud1,stud2; //定义两个同类的对象
stud2=stud1; //将stud1赋给stud2
说明:
- 对象的赋值只对其中的数据成员赋值,而不对成员函数赋值。数据成员是占存储空间的,不同对象的数据成员占有不同的存储空间,赋值的过程是将一个对象的数据成员在存储空间的状态复制给另一对象的数据成员的存储空间。而不同对象的成员函数是同一个函数代码段,不需要、也无法对它们赋值。
- 类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果
对象的复制
用一个已有的对象快速地复制出多个完全相同的对象。如
其一般形式为:
类名 对象2(对象1);
用对象1复制出对象2。
它与定义对象的方式类似,但是括号中给出的参数不是一般的变量,而是对象。在建立对象时调用一个特殊的构造函数——复制构造函数(copy constructor)。这个函数的形式是这样的:
//The copy constructor definition.
Box::Box(const Box& b)
{
height=b.height; width=b.width; length=b.length;
}
复制构造函数也是构造函数,但它只有一个参数,这个参数是本类的对象(不能是其他类的对象), 而且采用对象的引用的形式(一般约定加const声明,使参数值不能改变,以免在调用此函数时因不慎而使对象值被修改)。此复制构造函数的作用就是将实参对象的各成员值一一赋给新的对象中对应的成员。
如果用户自己未定义复制构造函数,则编译系统会自动提供一个默认的复制构造函数,其作用只是简单地复制类中每个数据成员。C++还提供另一种方便用户的复制形式,用赋值号代替括号,如
类名 对象名1 = 对象名2;
Box box2=box1; //用box1初始化box2
对象的赋值是对一个已经存在的对象赋值,因此必须先定义被赋值的对象,才能进行赋值。而对象的复制则是从无到有地建立一个新对象,并使它与一个已有的对象完全相同(包括对象的结构和成员的值)。
普通构造函数和复制构造函数的区别
1) 在形式上
类名(形参表列); //普通构造函数的声明,如Box(int h,int w,int len);
类名(类名& 对象名); //复制构造函数的声明,如Box(Box &b);
2) 在建立对象时,实参类型不同
系统会根据实参的类型决定调用普通构造函数或复制构造函数。如
Box box1(12,15,16); //实参为整数,调用普通构造函数
Box box2(box1); //实参是对象名,调用复制构造函数
3) 在什么情况下被调用
普通构造函数在程序中建立对象时被调用。复制构造函数在用已有对象复制一个新对象时被调用,在以下3种情况下需要克隆对象:
① 程序中需要新建立一个对象,并用另一个同类的对象对它初始化,如上面介绍的那样。
② 当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个实参的拷贝,这就是按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。如
void fun(Box b) //形参是类的对象
{ }
int main( )
{
Box box1(12,15,18);
fun(box1); //实参是类的对象,调用函数时将复制一个新对象b
return 0;
}
③ 函数的返回值是类的对象。在函数调用完毕将返回值带回函数调用处时。此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。如
Box f( ) //函数f的类型为Box类类型
{
Box box1(12,15,18);
return box1; //返回值是Box类的对象
}
int main( )
{
Box box2; //定义Box类的对象box2
box2=f( ); //调用f函数,返回Box类的临时对象,并将它赋值给box2
}