58 – 对象

58 – 对象

一、对象的概念

类的对象就是该类的某一特定实体,也称为实例。类是一种抽象的类型模板,而对象是这个模板的具体产物。正如”建筑图纸”和”按图纸建好的房子”之间的关系一样——图纸只有一个,但房子可以建很多栋。

声明一个对象与声明一个普通变量语法相同:

类型名  对象名;

例如:

Clock myClock;    // 声明一个 Clock 类型的对象Code language: JavaScript (javascript)

二、对象的内存布局

需要明确的是:对象所占据的内存空间只用于存放数据成员,函数成员不在每个对象中存储副本。

Clock c1;
Clock c2;
Clock c3;

上述三个对象各自拥有独立的 hour、minute、second 数据空间,但 ShowTime()、SetTime() 等成员函数的代码在内存中只有一份,被所有 Clock 对象共享。

原因很简单:函数代码是只读的指令序列,不会随对象状态变化而变化。如果每个对象都复制一份函数代码,将造成极大的内存浪费。

内容每个对象是否独立存储说明
数据成员是每个对象有各自的值
成员函数代码否(共享一份)所有对象调用同一份代码
静态数据成员否(共享一份)属于类,不属于某个对象

三、对象成员的访问

定义了类及其对象后,就可以通过成员选择运算符 .​ 来访问对象的成员。

  • 访问数据成员:对象名.数据成员名
  • 调用函数成员:对象名.函数成员名(参数表)
myClock.SetTime(12, 34, 20);   // 调用成员函数
myClock.ShowTime();             // 调用成员函数
// myClock.hour = 12;           // 错误:hour 是 private,不能通过 . 直接访问Code language: JavaScript (javascript)

注意:只有通过 . 访问 public 成员才是合法的。private 成员在类外不可见,即使使用 . 也无法访问。

四、代码示例

以下代码演示对象的创建、初始化和成员访问:

#include <iostream>
using namespace std;

class Clock
{
private:
    int hour, minute, second;

public:
    void SetTime(int h, int m, int s)
    {
        if (h >= 0 && h < 24) hour = h;
        else hour = 0;

        if (m >= 0 && m < 60) minute = m;
        else minute = 0;

        if (s >= 0 && s < 60) second = s;
        else second = 0;
    }

    void ShowTime()
    {
        if (hour < 10) cout << "0";
        cout << hour << ":";

        if (minute < 10) cout << "0";
        cout << minute << ":";

        if (second < 10) cout << "0";
        cout << second << endl;
    }

    void ShowTime1()
    {
        cout << "(ShowTime1)当前时间:";
        if (hour < 10) cout << "0";
        cout << hour << ":";

        if (minute < 10) cout << "0";
        cout << minute << ":";

        if (second < 10) cout << "0";
        cout << second << endl;
    }
};

int main()
{
    int jd;

    Clock c;     // 创建对象 c
    Clock c2;    // 创建对象 c2

    c.SetTime(12, 34, 20);     // 通过对象 c 调用公有成员函数
    c.ShowTime();              // 输出:12:34:20

    c2.SetTime(8, 5, 9);
    c2.ShowTime();             // 输出:08:05:09
    c2.ShowTime1();            // 输出:(ShowTime1)当前时间:08:05:09

    // c.hour = 12;            // 错误:hour 是 private,类外不可访问

    cin >> jd;
    cout << sin(jd) << "\n";
    cout << cos(jd) << "\n";
    cout << tan(jd) << "\n";

    return 0;
}Code language: PHP (php)

分析:

  • Clock c; 和 Clock c2; 创建了两个独立的 Clock 对象,各自拥有独立的 hour、minute、second。
  • c.SetTime(12, 34, 20) 调用的是共享的成员函数代码,但函数内部操作的 hour、minute、second 是 c 对象自己的数据。
  • c2 调用同样的 ShowTime(),操作的是 c2 自己的数据,输出不同的结果。
  • c.hour = 12; 如果取消注释会编译报错,证明 private 成员在类外即使通过 . 也无法访问。

五、类定义与对象声明的顺序

在 C++ 中,使用类之前必须先让编译器看到完整的定义。也就是说,类的定义必须出现在任何使用该类的对象声明之前。

// 错误写法:类定义在使用之后
int main()
{
    Clock c;        // 错误:Clock 尚未定义
    return 0;
}

class Clock
{
    // ...
};Code language: JavaScript (javascript)
// 正确写法:类定义在使用之前
class Clock
{
public:
    void SetTime(int h, int m, int s);
    void ShowTime();
private:
    int hour, minute, second;
};

int main()
{
    Clock c;        // 正确:Clock 已定义
    c.SetTime(9, 0, 0);
    c.ShowTime();
    return 0;
}Code language: JavaScript (javascript)

这也是为什么在实际工程中,类的定义通常放在头文件(.h)中,源文件(.cpp)包含该头文件后再使用类。

六、对象数组

可以像基本类型数组一样,声明对象数组:

Clock clocks[3];    // 创建包含 3 个 Clock 对象的数组

clocks[0].SetTime(8, 0, 0);
clocks[1].SetTime(12, 0, 0);
clocks[2].SetTime(18, 30, 0);

for (int i = 0; i < 3; i++)
{
    clocks[i].ShowTime();
}Code language: JavaScript (javascript)

每个数组元素都是一个独立的 Clock 对象,拥有各自的数据成员。

七、对象指针

可以声明指向对象的指针,通过箭头运算符 -> 访问成员:

Clock c;
Clock* p = &c;

p->SetTime(10, 20, 30);    // 等价于 (*p).SetTime(10, 20, 30)
p->ShowTime();Code language: PHP (php)
访问方式适用场景
对象名.成员名对象直接访问
指针名->成员名对象指针访问
(*指针名).成员名对象指针解引用后访问(等价写法)

八、注意事项

  • 对象声明时,编译器为数据成员分配内存,但数据成员的值是未初始化的(随机值),除非提供了构造函数或类内初始值。
  • 类的成员函数内部通过隐含的 this 指针区分当前调用对象,所以共享代码能正确操作不同对象的数据。
  • 访问成员时,. 和 -> 的左边类型决定了能访问哪些成员(只看该成员是否为 public)。
  • 不能对没有对象的类直接调用非静态成员函数:Clock::ShowTime(); 是错误的(缺少对象)。
  • 对象之间可以相互赋值(默认逐成员复制),但涉及指针成员时需要注意深拷贝问题(后续章节讲解)。

九、常见错误

1. 类定义放在对象声明之后
int main()
{
    Clock c;    // 错误:Clock 未定义
    return 0;
}

class Clock { ... };Code language: JavaScript (javascript)
2. 试图通过对象访问私有成员
Clock c;
c.hour = 10;              // 错误:hour 是 private
cout << c.minute;         // 错误Code language: JavaScript (javascript)
3. 混淆 . 和 ->
Clock c;
Clock* p = &c;

c->ShowTime();            // 错误:c 是对象,不是指针
p.ShowTime();             // 错误:p 是指针,不是对象Code language: PHP (php)

正确写法:

c.ShowTime();             // 对象用 .
p->ShowTime();            // 指针用 ->Code language: PHP (php)
4. 忘记类定义末尾的分号导致后续声明出错
class Clock
{
    // ...
}                         // 缺少分号

int main()
{
    Clock c;              // 可能报错:expected ';' after class definition
}Code language: JavaScript (javascript)

十、小结

  • 对象是类的实例,声明语法与变量相同:类名 对象名;
  • 对象内存只存放数据成员,成员函数代码只有一份,被所有对象共享。
  • 通过 . 运算符访问对象的 public 成员,通过 -> 运算符访问对象指针指向的成员。
  • private 成员在类外即使通过 . 也无法访问,只能通过 public 接口间接操作。
  • 类定义必须出现在对象声明之前,否则编译器无法识别类型。
  • 下一节将学习构造函数与析构函数,解决对象初始化和清理的问题。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注