往期回顾:
C++ 入门03:函数与作用域-CSDN博客
C++ 入门04:数组与字符串-CSDN博客
C++ 入门05:类和对象-CSDN博客
一、前言
在前面文章的学习中,我们了解了 C++ 的基本结构、变量、输入输出、控制结构、循环、函数、作用域、数组与字符串以及类和对象的基础知识。今天,我们将深入学习类的进阶内容,特别是构造函数的重载与拷贝构造函数。
二、类的进阶
2.1、 构造函数的重载
(1)什么是构造函数重载
构造函数的重载是面向对象编程中一个非常有用的特性,它允许在同一个类中定义多个构造函数,这些构造函数具有相同的名称(即与类名相同)但参数列表不同。参数列表的不同可以体现在参数的数量、类型或顺序上。这种设计允许开发者根据不同的初始化需求,以灵活的方式创建对象实例。
(2)为什么需要构造函数的重载?
-
提高代码灵活性:
构造函数的重载提供了一种灵活的方式来初始化对象。不同的构造函数可以处理不同的初始化场景,从而避免在单个构造函数中使用复杂的逻辑判断来决定如何根据提供的参数初始化对象。例如,一个Person
类可能提供多个构造函数,一个接受名字和年龄,另一个只接受名字,还有一个无参构造函数用于创建默认状态的Person
对象。 -
增强代码可读性:
通过为不同的初始化场景提供明确的构造函数,可以使得代码更加清晰易懂。调用者可以根据需要直接选择合适的构造函数来创建对象,而无需担心构造函数内部的复杂逻辑。 -
支持多种初始化方式:
在某些情况下,对象的初始化可能依赖于外部数据源或配置文件的内容。通过重载构造函数,可以设计不同的构造函数来接受不同的数据源或配置方式,从而使得类的使用更加灵活和通用。 -
提高类的可重用性:
通过重载构造函数,可以使得类更容易被其他类继承和使用。子类可以根据需要覆盖或扩展父类的构造函数,以提供额外的初始化逻辑或支持新的初始化场景。 -
符合设计原则:
构造函数的重载也符合面向对象设计中的一些基本原则,如单一职责原则和开闭原则。单一职责原则要求一个类只负责一件事情,通过重载构造函数,我们可以将初始化对象的不同方式分散到不同的构造函数中,从而使得每个构造函数都专注于一种特定的初始化场景。开闭原则则要求软件实体对扩展开放,对修改关闭。通过重载构造函数,我们可以在不修改现有代码的情况下添加新的初始化方式,从而符合开闭原则的要求。
示例:
#include <iostream>
using namespace std;
class Car {
public:
string brand;
string model;
int year;
// 默认构造函数
Car() {
brand = "Unknown";
model = "Unknown";
year = 0;
}
// 带参数的构造函数
Car(string b, string m, int y) {
brand = b;
model = m;
year = y;
}
// 只提供品牌和年份的构造函数
Car(string b, int y) {
brand = b;
model = "Unknown";
year = y;
}
void displayInfo() {
cout << "Brand: " << brand << endl;
cout << "Model: " << model << endl;
cout << "Year: " << year << endl;
}
};
int main() {
Car car1; // 调用默认构造函数
Car car2("Toyota", "Corolla", 2020); // 调用带参数的构造函数
Car car3("Honda", 2018); // 调用只提供品牌和年份的构造函数
car1.displayInfo();
car2.displayInfo();
car3.displayInfo();
return 0;
}
在这个示例中,我们定义了三个构造函数:
默认构造函数:不接受任何参数,初始化所有属性为默认值。 |
带参数的构造函数:接受品牌、型号和年份参数。 |
只接受品牌和年份的构造函数:型号设置为默认值 "Unknown"。 |
2.2.、拷贝构造函数
(1)什么是拷贝构造函数
拷贝构造函数是C++中一个非常关键的特殊成员函数,它的主要作用是创建并初始化一个新对象,该新对象是通过复制另一个已存在的同类型对象(源对象)来完成的。这个复制过程不仅仅是简单地复制对象中的每个字节,而是根据对象的类型和实际需求来执行更加复杂的操作。
拷贝构造函数的名字与类名相同,但它接受一个指向该类型对象的常量引用作为参数。这个参数是源对象的引用,用于从源对象复制数据到新对象中。由于参数是常量引用,因此拷贝构造函数不会修改源对象。
class MyClass {
public:
// ... 其他成员 ...
// 拷贝构造函数
MyClass(const MyClass& other) {
// 复制操作
}
};
(2)为什么需要拷贝构造函数?
-
动态内存管理:
当类的对象包含指向动态分配内存的指针时,默认的拷贝构造函数只会复制指针的值,而不会复制指针所指向的内存区域。这会导致两个对象共享同一块内存区域,从而引发内存泄漏或双重释放等问题。通过自定义拷贝构造函数,我们可以确保每个对象都拥有自己独立的内存区域,避免这些问题。 -
深拷贝与浅拷贝:
拷贝构造函数允许我们实现深拷贝或浅拷贝。深拷贝意味着除了复制对象的值之外,还复制了对象内部所有动态分配的内存或其他资源。浅拷贝则只复制了对象的值,而没有复制其内部资源。根据对象的特性和需求,我们可以选择实现深拷贝或浅拷贝。 -
资源管理:
除了动态内存之外,对象还可能管理其他类型的资源,如文件句柄、网络连接等。通过自定义拷贝构造函数,我们可以确保这些资源在对象复制时得到正确的管理,例如为每个新对象分配新的资源实例或共享资源(但通常不推荐共享资源,因为它可能引入复杂的同步和并发问题)。 -
性能优化:
在某些情况下,我们可能希望优化对象的复制过程以提高性能。通过自定义拷贝构造函数,我们可以实现更高效的复制策略,例如使用移动语义(在C++11及更高版本中通过移动构造函数和移动赋值运算符实现)来避免不必要的复制操作。 -
符合设计原则:
自定义拷贝构造函数使得类的行为更加清晰和可预测。它允许类的设计者精确地控制对象复制时的行为,从而符合面向对象设计中的一些基本原则,如封装性、一致性和可维护性。
示例:
#include <iostream>
using namespace std;
class Car {
public:
string brand;
string model;
int year;
// 带参数的构造函数
Car(string b, string m, int y) {
brand = b;
model = m;
year = y;
}
// 拷贝构造函数
Car(const Car &oldCar) {
brand = oldCar.brand;
model = oldCar.model;
year = oldCar.year;
}
void displayInfo() {
cout << "Brand: " << brand << endl;
cout << "Model: " << model << endl;
cout << "Year: " << year << endl;
}
};
int main() {
Car car1("Toyota", "Corolla", 2020);
Car car2 = car1; // 使用拷贝构造函数
car1.displayInfo();
car2.displayInfo();
return 0;
}
在这个示例中,我们定义了一个拷贝构造函数,它接受一个 Car
类的对象作为参数,并复制其属性值。这样,car2
对象就是通过复制 car1
对象来初始化的。
以上就是 C++ 程序的构造函数的重载与拷贝构造函数的基本用法的知识点了。通过构造函数的重载,我们可以用不同的方式初始化对象,而拷贝构造函数使我们能够复制对象并执行额外的操作。
都看到这里了,点个赞再走呗朋友~
加油吧,预祝大家变得更强!