前言
构造函数和析构函数的作用?
构造函数:创建类对象时,自动给对象成员变量进行初始化或者一些起始操作,例如打开文件等。使其能达到一种合适的状态。
析构函数:当对象的生命周期结束后,可以对其进行一些清理工作,比如释放内存或者关闭文件等操作。
一、默认初始化
当创建一个类的实例时,如果没有定义构造函数,系统会自动生成一个无参构造函数,并自动初始化成员变量。内置类型的变量(如int、double、指针等)会被初始化为一个未定义的值,而自定义类型的变量(如类、结构体)会调用默认构造函数进行初始化。
代码示例:
class MyClass {
public:
int num;
std::string str;
MyClass() {} // 默认构造函数
};
MyClass obj; // 默认初始化对象,num和str的值赋为默认值
二、有参构造函数
列表初始化:使用花括号 {} 进行初始化,提供了更严格的类型匹配和防止窄化转换的特性。
代码示例;
class MyClass {
public:
int num;
std::string str;
MyClass(int n, const std::string& s) : num(n), str(s) {} // 带参数的构造函数
};
MyClass obj{10, "Hello"}; // 列表初始化对象,使用参数值进行初始化
三、拷贝构造函数
拷贝构造函数是用来创建一个对象的副本的特殊构造函数。在C++中,拷贝构造函数通常有以下几种实现方式:
1、浅拷贝:简单地将一个对象的成员变量逐个复制到另一个对象中。这种方式适用于没有指针等动态分配内存的简单对象。 ·
ClassName(const ClassName& other) {//常量引用,防止修改
member1 = other.member1;
member2 = other.member2;
// 其他成员变量的赋值
}
2、深拷贝:当对象中有指针等动态分配内存的成员变量时,需要进行深拷贝,即重新分配内存并将原对象的数据复制过去。
ClassName(const ClassName& other) {
member1 = new int(*other.member1); // 假设member1是int类型指针
member2 = new char(*other.member2); // 假设member2是char类型指针
// 其他成员变量的深拷贝
}
注:浅拷贝构造函数和赋值运算符重载可以由系统提供,可以不写,不过如果涉及到成员变量是指针的情况,需要采用深拷贝,系统无法提供,需要手动写入。
另外需要分辨出浅拷贝和赋值拷贝的区别
代码示例:
#include <iostream>
#include <string>
using namespace std;
class Person{
private:
string name;
int age;
public:
Person():name{},age{}{}
Person(string name,int age):name{name},age{age}{}
Person(const Person &other){
this->age = other.age;
this->name = other.name;
}
Person &operator=(Person &other)
{
if(this != &other)//排除自己给自己赋值
{
this->age = other.age;
this->name = other.name;
}
return *this;//返回当前对象才能连续赋值
}
string getName()const
{
return name;
}
int getAge()const
{
return age;
}
};
int main()
{
Person p1("zhangsan",20);
Person p2 = p1;//初始化,调用拷贝构造函数
Person p3("list",21);
Person p4(p3);//初始化,调用拷贝构造函数
p3 = p1;//赋值,调用赋值运算符重载
cout<<p1.getName()<<p1.getAge()<<endl;
cout<<p2.getName()<<p2.getAge()<<endl;
cout<<p3.getName()<<p3.getAge()<<endl;
cout<<p4.getName()<<p4.getAge()<<endl;
return 0;
}
运行结果:
zhangsan20
zhangsan20
zhangsan20
list21
四、初始化顺序
在C++类中声明变量并进行初始化时,变量的初始化顺序与在构造函数的初始化列表中列出的顺序无关。实际上,变量的初始化顺序取决于它们在类中的声明顺序。
代码示例:
class MyClass{
public:
MyClass():b(1),a(b){}
void printValues(){
cout<<"a:"<<a<<",b:"<<b<<endl;
}
private:
int a;
int b;
};
int main() {
MyClass obj;
obj.printValues();
return 0;
}
运行结果:初始化顺序按照类中的声明顺序,a初始化赋b值,但因为b还未初始化,导致a未初始化直接输出。
四、RAII (Resource Acquisition Is Initialization)
1、RALL概述
顾名思义,资源获取就直接初始化,这是C++中的一种编程技术,它将资源管理和对象生命周期绑定在一起。具体而言,资源(如内存、文件句柄、网络连接等)在对象构造时获取,在对象析构时释放。这种方式确保了即使在异常情况下也能正确地管理资源,避免了资源泄露和其他相关问题。
简单来说,就是通过构造函数和析构函数,动态分配和释放资源,避免资源泄露和其他相关问题。
2、RAII的应用场景
RAII广泛应用于多种资源管理场合,包括但不限于:
•内存分配与释放
•文件打开与关闭
•线程同步(锁的获取与释放)
•数据库连接的建立与断开
3、实现原理和方法:
•系统资源(如文件描述符)的管理RAII的实现原理RAII利用C++的构造函数和析构函数来自动管理资源。对象在其生命周期内持有资源,构造函数负责资源的获取,析构函数负责资源的释放。