Bootstrap

C++ 类对象初始化

前言

构造函数和析构函数的作用?

构造函数:创建类对象时,自动给对象成员变量进行初始化或者一些起始操作,例如打开文件等。使其能达到一种合适的状态。

析构函数:当对象的生命周期结束后,可以对其进行一些清理工作,比如释放内存或者关闭文件等操作。

一、默认初始化

当创建一个类的实例时,如果没有定义构造函数,系统会自动生成一个无参构造函数,并自动初始化成员变量。内置类型的变量(如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++的构造函数和析构函数来自动管理资源。对象在其生命周期内持有资源,构造函数负责资源的获取,析构函数负责资源的释放。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;