一·、 什么是构造函数
类的
构造函数
是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造,那构造的是什么呢?
构造成员变量的初始化值,内存空间等
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回
void
。构造函数可用于 为某些成员变量设置初始值。
下面的实例有助于更好地理解构造函数的概念:
#include <iostream>
#include <string>
using namespace std; // 使用std命名空间
class Car {
public:
string brand; // 不需要使用std::string
int year;
// 无参构造函数
Car() {
brand = "未知";
year = 0;
cout << "无参构造函数被调用" << endl; // 不需要使用std::cout和std::endl
}
void display() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
Car myCar; // 创建Car对象
myCar.display(); // 显示车辆信息
return 0;
}
2.7.2
带参数构造函数
默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:
#include <iostream>
#include <string>
using namespace std; // 使用std命名空间
class Car {
public:
string brand; // 不需要使用std::string
int year;
// 无参构造函数
Car(string b,int y) {
brand=b;
year=y;
cout << "有参构造函数被调用" << endl; // 不需要使用std::cout和std::endl
}
void display() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
Car myCar("奥迪",2012); // 创建Car对象
myCar.display(); // 显示车辆信息
return 0;
}
二、使用初始化列表
在
C++
中,使用初始化列表来初始化类的字段是一种高效的初始化方式,尤其在构造函数中。初始化列表直接在对象的构造过程中初始化成员变量,而不是先创建成员变量后再赋值。这对于提高性能尤其重要,特别是在涉及到复杂对象或引用和常量成员的情况下。
初始化列表紧跟在构造函数参数列表后面,以冒号(
:
)开始,后跟一个或多个初始化表达式,每个表达式通常用逗号分隔。下面是使用初始化列表初始化字段的例子:
#include <iostream>
#include <string>
using namespace std; // 使用std命名空间
class Car {
public:
string brand; // 不需要使用std::string
int year;
// 参数列表构造函数
Car(string b,int y) : brand(b),year(y) {
cout << "参数列表构造函数被调用" << endl; // 不需要使用std::cout和std::endl
}
void display() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
Car myCar("奥迪",2012); // 创建Car对象
myCar.display(); // 显示车辆信息
return 0;
}
在这个例子中, myCar 有三个成员变量: brand ( int 类型)、 year( double 类型)。当创建 myCar的一个实例时,我们通过构造函数传递三个参数,这些参数
被用于通过初始化列表直接初始化成员变量。初始化列表 : brand(x), year(y)的意思是用 x 初始化
brand ,用
y
初始化 year
。
初始化列表的优点包括:
1.
效率
:对于非基本类型的对象,使用初始化列表比在构造函数体内赋值更高效,因为它避免了先默认构造然后再赋值的额外开销。
2.
必要性
:对于
引用类型和常量类型
的成员变量,必须使用初始化列表,因为这些类型的成员变量在构造函数体内不能被赋值。
3.
顺序
:成员变量的初始化顺序是按照它们在类中声明的顺序,而不是初始化列表中的顺序。
使用初始化列表是
C++
中推荐的初始化类成员变量的方式,因为它提供了更好的性能和灵活性。
三、new关键字
在
C++
中,
new
关键字用于动态分配内存。它是
C++
中处理动态内存分配的主要工具之一,允许在程序运行时根据需要分配内存。
基本用法
分配单个对象
:使用
new
可以在堆上动态分配一个对象。例如,
new int
会分配一个
int
类型的空
间,并返回一个指向该空间的指针。
int* ptr = new int ; //C 语言中, int *p = (int *)malloc(sizeof(int));
分配对象数组
:
new
也可以用来分配一个对象数组。例如,
new int[10]
会分配一个包含
10
个整数的数组。
int* arr = new int [ 10 ]; //C 语言中, int *arr = (int *)malloc(sizeof(int)*10);
初始化
:可以在
new
表达式中使用初始化。对于单个对象,可以使用构造函数的参数:
MyClass * obj = new MyClass ( arg1 , arg2 );
与
delete
配对使用
使用
new
分配的内存必须显式地通过
delete
(对于单个对象)或
delete[]
(对于数组)来释放,以避免内存泄露:
释放单个对象:
delete ptr ; // 释放 ptr 指向的对象
释放数组:
delete [] arr ; // 释放 arr 指向的数组
注意事项
异常安全
:如果
new
分配内存失败,它会抛出
std::bad_alloc
异常(除非使用了
nothrow
版
本)。
内存泄露
:忘记释放使用
new
分配的内存会导致内存泄露。
匹配使用
delete
和
delete[]
:为避免未定义行为,使用
new
分配的单个对象应该使用
delete
释放,使用
new[]
分配的数组应该使用
delete[]
释放。
示例代码
#include <iostream>
#include <string>
class MyClass {
public:
MyClass() {
std::cout << "Object created" << std::endl;
}
};
int main() {
// 分配单个对象
MyClass* myObject = new MyClass();
// 分配对象数组
int* myArray = new int[5]{1, 2, 3, 4, 5};
// 使用对象和数组...
// 释放内存
delete myObject;
delete[] myArray;
return 0;
}
在这个例子中,
new
被用来分配一个
MyClass
类型的对象和一个整数数组,然后使用
delete
和
delete[]
来释放内存。每个
new
都对应一个
delete
,保证了动态分配的内存被适当管理。
四、
析构函数
(1)什么是析构函数
析构函数是
C++
中的一个特殊的成员函数,它在对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。析构函数特别重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。
(2)基本特性
1.
名称
:析构函数的名称由波浪号(
~
)后跟类名构成,如
~MyClass()
。
2.
无返回值和参数
:析构函数不接受任何参数,也不返回任何值。
3.
自动调用
:当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用
delete
删除一个动态分配的对象),析构函数会被自动调用。
4.
不可重载
:每个类只能有一个析构函数。
5.
继承和多态
:如果一个类是多态基类,其析构函数应该是虚的。
示例
假设我们有一个类
MyClass
,它包含了动态分配的内存或其他资源:
#include <iostream>
#include <string>
class MyClass {
private:
int *datas;
public:
MyClass(int size) {
datas = new int[size];
}
~MyClass() {
std::cout << "析构函数执行" << std::endl;
delete[] datas;
}
};
int main() {
// 分配单个对象
MyClass m1(10);
MyClass* m2 = new MyClass(10);
delete m2;
return 0;
}
在这个示例中, MyClass 的构造函数分配了一块内存,而析构函数释放了这块内存。当 obj 的生命周期结束时(即离开了它的作用域), MyClass 的析构函数被自动调用,负责清理资源,防止内存泄露。
(3)重要性
析构函数在管理资源方面非常重要。没有正确实现析构函数,可能导致资源泄露或其他问题。在基于
RAII (资源获取即初始化)原则的C++
编程实践中,确保资源在对象析构时被适当释放是非常关键的。当使用
智能指针和其他自动资源管理技术时,可以减少显式编写析构函数的需要,但了解析构函数的工作原理 仍然很重要。
以下是关于
C++
中析构函数需要了解的十个要点的表格:
标注粗体部分,是能快速上手的内容,方便后续
QT
的学习,而没有粗体的部分,会在
QT
结束后,如果安 排C++
深入讲解的课程的话,会安排到。 同理拷贝构造函数,考虑到学习策略安排,这里先陈列出来。