6.1 对象指针和对象引用

 

6.1 对象指针和对象引用

第6章 类和对象(二)

前一章中讲述了类和对象的一些基础知识,本章将进一步讨论类和对象的其他方面的内容。这些内容包括指针和引用在类和对象中的应用,使用 newdelete 运算符对对象进行动态分配和释放等。本章通过一些实例来深入理解类和对象在编程中的应用。

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

说明

  1. 该程序中定义了一个指向类的数据成员的指针 pc 和一个指向类成员函数的指针 pfun,并对这些指针进行了赋值。

  2. 程序中对指向类成员的两个指针进行了引用和调用。其中:

    x.*pc = 3;
    

    实际上是给对象 x 的成员 c 赋值,等价于:

    x.c = 3;
    

    (p->*pfun)(5)
    

    是通过指向对象的指针 p 来调用指向类成员函数的指针 pfun,函数的实参为 5。等价于:

    p->fun(5)
    

    也可以使用对象来调用指向类成员函数的指针所指向的成员函数。例如:

    (x.*pfun)(5)
    

  3. 程序中使用了两种不同的指针:一种是指向对象的指针,另一种是指向类成员的指针。虽然它们都是指针,但指向的目标不同。p 是指向类的对象,pc 是指向类的数据成员,pfun 是指向类的成员函数,它们在使用上不同。

 

 

6.1.2 对象指针和对象引用作为函数参数

1. 对象指针作为函数参数

使用对象指针作为函数参数比使用对象本身更为普遍。其优点如下:

  1. 实现传址调用:可以在被调用函数中改变调用函数的参数对象的值,从而实现函数之间的信息传递。
  2. 提高运行效率:使用对象指针作为形参仅将对象的地址值传给形参,而不进行对象副本的复制,减少了时空开销。

对象指针作为函数形参时,要求调用函数的实参是对象的地址值。下面通过一个例子来说明使用指向对象的指针作为函数参数的方法。

例 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

说明:

  1. 该程序中,有两个指向对象的指针。一个是用于成员函数 copy() 的参数,另一个是用于一般函数 fun() 的参数。当形参是指向对象的指针时,调用函数的对应实参应该是某个对象的地址值,通常使用 & 符号加上对象名。
  2. 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 指针。

  1. 判断自身赋值

    if (this == &aa)
        return;
    
    • 这段代码用于判断 this 指针是否与传入的对象 aa 的地址相同。如果相同,则说明是自赋值,自赋值没有意义,因此直接返回。
  2. 赋值操作

    *this = aa;
    
    • 这里的 *this 表示操作该成员函数的对象,将 aa 对象的值赋给操作该成员函数的对象。由于 this 是指针,*this 就是指向的对象。

main() 函数中,a1.copy(a2); 调用 copy() 成员函数,此时 this 指向 a1 对象,aaa2 对象。执行 *this = aa; 语句时,将 a2 的值赋给 a1,因此 a1 的数据成员 ab 被赋值为 a2 的值。

最后,调用 a1.print(); 输出结果为 3, 4

通过这个例子,可以更好地理解 this 指针的用途,尤其是在需要处理对象自赋值和操作当前对象的情况下。

 

 

 

 

 

 

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值