6.1 对象指针和对象引用
第6章 类和对象(二)
前一章中讲述了类和对象的一些基础知识,本章将进一步讨论类和对象的其他方面的内容。这些内容包括指针和引用在类和对象中的应用,使用 new
和 delete
运算符对对象进行动态分配和释放等。本章通过一些实例来深入理解类和对象在编程中的应用。
6.1 对象指针和对象引用
本节讲述指向对象的指针和对象的引用这两个概念及其在 C++ 编程中的应用。在讨论这两个概念之前,先介绍指向类的成员的指针。虽然它们都是指针,但指向的目标不同。
6.1.1 指向类的成员的指针
在 C++ 中,指向类成员的指针包括指向类的数据成员和指向成员函数的指针。
指向数据成员的指针格式如下:
<类型说明符> <类名>::* <指针名>
指向成员函数的指针格式如下:
<类型说明符> (<类名>::*<指针名>)(<参数表>)
例如,定义一个类 A
如下:
class A {
public:
int fun(int b) { return a * c + b; }
A(int i) { a = i; }
int c;
private:
int a;
};
定义一个指向类 A
的数据成员 c
的指针 pc
,其格式如下:
int A::*pc = &A::c;
因为 c
是公有成员,因此可以这样定义。这里 pc
是指向 A
类的数据成员 c
的指针。
再定义一个指向类 A
的成员函数 fun
的指针 pfun
,其格式如下:
int (A::*pfun)(int) = &A::fun;
使用这类指针时,需要首先指定 A
类的一个对象,然后通过对象来引用指针所指向的成员。例如,给 pc
指针所指向的数据成员 c
赋值 8,可以表示如下:
A a;
a.*pc = 8;
其中,运算符 .*
用于通过指向类成员的指针来操作该类对象的成员。
使用指向对象的指针通过指向类成员的指针对该成员进行操作时,可使用运算符 ->*
。例如:
A* p = &a;
p->*pc = 8;
代码示例
下面是一个使用指向类成员指针的例子:
#include <iostream>
class A {
public:
A(int i) { a = i; }
int fun(int b) { return a * c + b; }
int c;
private:
int a;
};
void main() {
A x(8);
int A::*pc = &A::c;
x.*pc = 3;
int (A::*pfun)(int) = &A::fun;
A* p = &x;
std::cout << (p->*pfun)(5) << std::endl;
}
执行该程序输出结果如下:
29
说明
-
该程序中定义了一个指向类的数据成员的指针
pc
和一个指向类成员函数的指针pfun
,并对这些指针进行了赋值。 -
程序中对指向类成员的两个指针进行了引用和调用。其中:
x.*pc = 3;
实际上是给对象
x
的成员c
赋值,等价于:x.c = 3;
而
(p->*pfun)(5)
是通过指向对象的指针
p
来调用指向类成员函数的指针pfun
,函数的实参为5
。等价于:p->fun(5)
也可以使用对象来调用指向类成员函数的指针所指向的成员函数。例如:
(x.*pfun)(5)
-
程序中使用了两种不同的指针:一种是指向对象的指针,另一种是指向类成员的指针。虽然它们都是指针,但指向的目标不同。
p
是指向类的对象,pc
是指向类的数据成员,pfun
是指向类的成员函数,它们在使用上不同。
6.1.2 对象指针和对象引用作为函数参数
1. 对象指针作为函数参数
使用对象指针作为函数参数比使用对象本身更为普遍。其优点如下:
- 实现传址调用:可以在被调用函数中改变调用函数的参数对象的值,从而实现函数之间的信息传递。
- 提高运行效率:使用对象指针作为形参仅将对象的地址值传给形参,而不进行对象副本的复制,减少了时空开销。
对象指针作为函数形参时,要求调用函数的实参是对象的地址值。下面通过一个例子来说明使用指向对象的指针作为函数参数的方法。
例 6.2 分析下列程序的输出结果:
#include <iostream>
class M {
public:
M() { x = y = 0; }
M(int i, int j) { x = i; y = j; }
void copy(M *m);
void setxy(int i, int j) { x = i; y = j; }
void print() { std::cout << x << "," << y << std::endl; }
private:
int x, y;
};
void M::copy(M *m) {
x = m->x;
y = m->y;
}
void fun(M m1, M *m2);
int main() {
M p(5, 7), q;
q.copy(&p);
fun(p, &q);
p.print();
q.print();
}
void fun(M m1, M *m2) {
m1.setxy(12, 15);
m2->setxy(22, 25);
}
输出结果:
5,7
22,25
说明:
- 该程序中,有两个指向对象的指针。一个是用于成员函数
copy()
的参数,另一个是用于一般函数fun()
的参数。当形参是指向对象的指针时,调用函数的对应实参应该是某个对象的地址值,通常使用&
符号加上对象名。 - 在
fun()
函数中,有两个形参,一个是对象名,另一个是指向对象的指针名。在被调用函数中,改变了对象的数据成员值和指向对象指针的数据成员值以后,可以看到只有指向对象指针作为参数所指向的对象被改变了;而另一个以对象作为参数,形参对象值改变了,但实参对象值并没有改变。因此,该程序出现上述输出结果。这便是传址调用和传值调用的不同。
2. 对象引用作为函数参数
在实际应用中,使用对象引用作为函数参数比使用对象指针更普遍。这是因为使用对象引用作为函数参数既具有对象指针的优点,同时更简单直接。因此,C++ 编程中,人们更喜欢使用对象引用作为函数参数。
例 6.3 分析下列程序的输出结果:
#include <iostream>
class M {
public:
M() { x = y = 0; }
M(int i, int j) { x = i; y = j; }
void copy(M &m);
void setxy(int i, int j) { x = i; y = j; }
void print() { std::cout << x << "," << y << std::endl; }
private:
int x, y;
};
void M::copy(M &m) {
x = m.x;
y = m.y;
}
void fun(M m1, M &m2);
int main() {
M p(5, 7), q;
q.copy(p);
fun(p, q);
p.print();
q.print();
}
void fun(M m1, M &m2) {
m1.setxy(12, 15);
m2.setxy(22, 25);
}
输出结果:
5,7
22,25
说明: 将该程序与例 6.2 进行比较可以看出,主要区别在于例 6.2 中使用的指向对象的指针在例 6.3 中换成了对象引用。对象引用的使用更简洁直观,同时也具有指针的传址调用特性,从而在 C++ 编程中得到了更广泛的应用。
总结:
- 对象指针:实现传址调用,节省内存,提高效率,但需要处理指针的细节。
- 对象引用:实现传址调用,语法更简洁,避免了指针操作的复杂性,更符合面向对象编程的思想。
6.1.3 this指针
this
是一个由系统自动提供的指向对象的特殊指针。该指针是一个指向正在对某个成员函数操作的对象的指针。当对一个对象调用成员函数时,编译程序先将该对象的地址赋给系统创建的 this
指针,然后调用成员函数。每次成员函数存取数据成员时,都隐含使用 this
指针,同样也可以显式使用 *this
来标识调用该成员函数的对象。
下面通过一个例子来说明显式使用 this
指针的方法。
例 6.4 分析下列程序的输出结果,说明程序中 this
和 *this
的用法。
#include <iostream>
class A {
public:
A() { a = b = 0; }
A(int i, int j) { a = i; b = j; }
void copy(const A &aa);
void print() { std::cout << a << "," << b << std::endl; }
private:
int a, b;
};
void A::copy(const A &aa) {
if (this == &aa)
return;
*this = aa;
}
int main() {
A a1, a2(3, 4);
a1.copy(a2);
a1.print();
}
输出结果:
3,4
说明:
在该程序中,类 A 的成员函数 copy()
内,出现了两次 this
指针。
-
判断自身赋值:
if (this == &aa) return;
- 这段代码用于判断
this
指针是否与传入的对象aa
的地址相同。如果相同,则说明是自赋值,自赋值没有意义,因此直接返回。
- 这段代码用于判断
-
赋值操作:
*this = aa;
- 这里的
*this
表示操作该成员函数的对象,将aa
对象的值赋给操作该成员函数的对象。由于this
是指针,*this
就是指向的对象。
- 这里的
在 main()
函数中,a1.copy(a2);
调用 copy()
成员函数,此时 this
指向 a1
对象,aa
是 a2
对象。执行 *this = aa;
语句时,将 a2
的值赋给 a1
,因此 a1
的数据成员 a
和 b
被赋值为 a2
的值。
最后,调用 a1.print();
输出结果为 3, 4
。
通过这个例子,可以更好地理解 this
指针的用途,尤其是在需要处理对象自赋值和操作当前对象的情况下。