[C++]对象数组
1.对数组对象的理解:
在 C++ 中,对象数组是指存储类对象的一组连续的内存空间。它和普通的基本数据类型数组非常相似,不同的是,数组中的每个元素都是一个类对象。由于 C++ 支持面向对象编程,对象数组不仅存储数据,还能调用类中的成员函数和使用构造函数、析构函数等面向对象的特性。
class MyClass {
public:int value;// 构造函数MyClass(int val) {value = val;}// 打印成员变量void print() {cout << "Value: " << value << endl;}
};int main() {MyClass arr[3] = {MyClass(1), MyClass(2), MyClass(3)}; // 创建一个 MyClass 对象数组for (int i = 0; i < 3; i++) {arr[i].print(); // 调用每个元素的成员函数}return 0;
}/*输出
Value: 1
Value: 2
Value: 3
*/
注意: arr[3]
数组包含 3 个 MyClass
类型的对象。在创建这个数组时,C++ 会调用每个元素的构造函数,初始化数组中的对象。
2. 对象数组的初始化
对象数组的初始化和普通数组类似,但每个元素都需要调用构造函数。如果没有显式提供初值,则会调用默认构造函数(如果存在)。
- 显式初始化: 你可以通过数组初始化列表来初始化对象数组。
MyClass arr[3] = {MyClass(1), MyClass(2), MyClass(3)};
- 默认构造函数初始化: 如果你定义了一个默认构造函数,就可以用默认构造函数初始化对象数组。
class MyClass { public:int value;MyClass() { // 默认构造函数value = 0;}void print() {cout << "Value: " << value << endl;} };int main() {MyClass arr[3]; // 默认初始化for (int i = 0; i < 3; i++) {arr[i].print();}return 0; }/*输出 Value: 0 Value: 0 Value: 0 */
在此例中,数组
arr
中的每个对象都会调用MyClass
的默认构造函数,将value
初始化为0
。
3. 动态创建对象数组
C++ 中的对象数组通常是静态分配的,也就是说,在栈上定义对象数组。然而,你也可以在堆上动态分配对象数组。这时你需要使用 new
操作符来创建对象数组,并且手动管理内存(即使用 delete[]
释放内存)。
class MyClass {
public:int value;MyClass(int val) {value = val;}void print() {cout << "Value: " << value << endl;}
};int main() {MyClass* arr = new MyClass[3] { MyClass(1), MyClass(2), MyClass(3) }; // 动态分配对象数组for (int i = 0; i < 3; i++) {arr[i].print();}delete[] arr; // 手动释放内存return 0;
}/*输出
Value: 1
Value: 2
Value: 3
*/
在这里,new MyClass[3]
在堆上分配了一个包含 3 个 MyClass
对象的数组。每个元素调用了构造函数进行初始化。最后使用 delete[]
来释放内存。
4. 对象数组的内存布局
在对象数组中,数组的每个元素都是独立的对象,每个对象都有自己的数据成员。在内存中,整个数组是一个连续的内存区域,每个对象紧挨着前一个对象。内存布局类似于基础类型数组,区别在于每个元素不仅占用数据空间,还包含成员函数和成员变量的地址等。
5. 访问对象数组元素
对象数组的访问方式与普通数组相同,可以使用索引来访问对象数组的每个元素。通过访问数组元素,你可以访问该对象的成员函数和成员变量。
MyClass arr[3] = { MyClass(1), MyClass(2), MyClass(3) };
arr[1].print(); // 访问第二个对象的成员函数
6. 对象数组的拷贝和赋值
对象数组的赋值和拷贝通常需要调用类的拷贝构造函数。如果类没有显式定义拷贝构造函数,C++ 会自动生成一个默认的拷贝构造函数,该构造函数执行浅拷贝。
class MyClass {
public:int value;MyClass(int val) {value = val;}// 拷贝构造函数MyClass(const MyClass& other) {value = other.value;}void print() {cout << "Value: " << value << endl;}
};int main() {MyClass arr1[3] = { MyClass(1), MyClass(2), MyClass(3) };MyClass arr2[3]; // 创建另一个对象数组// 拷贝 arr1 到 arr2for (int i = 0; i < 3; i++) {arr2[i] = arr1[i]; // 调用拷贝构造函数或拷贝赋值操作符}// 打印 arr2 内容for (int i = 0; i < 3; i++) {arr2[i].print();}return 0;
}/*输出
Value: 1
Value: 2
Value: 3
*/
在这段代码中,通过 arr1
和 arr2
两个对象数组进行拷贝,每个元素都调用拷贝构造函数。
7. 注意事项
-
析构函数:对于对象数组中的每个元素,析构函数都会在对象销毁时被自动调用。如果对象数组是通过
new
动态分配的,记得使用delete[]
释放内存。 -
内存管理:在使用动态分配对象数组时,要特别注意内存管理,避免内存泄漏。每个对象在析构时会执行清理操作,动态分配的对象数组也要使用
delete[]
来释放内存。 -
拷贝构造函数:当对象数组被拷贝时,C++ 会调用拷贝构造函数。如果没有显式定义拷贝构造函数,C++ 会生成默认的拷贝构造函数,该函数会按成员逐一拷贝,但如果数组中的元素使用动态分配内存,可能会导致浅拷贝的问题。因此,有必要为类定义一个合适的拷贝构造函数。
8. 总结
- 对象数组是存储多个类对象的数组。它和普通数组一样,支持索引访问,每个元素都是类的一个实例。
- 对象数组的初始化可以通过默认构造函数、带参构造函数或拷贝构造函数来完成。
- 在使用动态分配的对象数组时,记得手动释放内存。
- 拷贝构造函数是非常重要的,尤其是在对象数组间进行赋值或拷贝时。