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接口间接操作。- 类定义必须出现在对象声明之前,否则编译器无法识别类型。
- 下一节将学习构造函数与析构函数,解决对象初始化和清理的问题。