C++类与对象--2 对象的初始化和清理

  • C++面向对象来源于生活,每个对象都有初始化设置和销毁前的清理数据的设置。

2.1 构造函数和析构函数

(1)构造函数

  • **++初始化++**对象的成员属性
  • 不提供构造函数时,编译器会提供不带参数的默认构造函数,函数实现是空的
  • 构造函数不需要调用,编译器会自动调用
cpp 复制代码
class Test
{
    private:
        int m_var;
    public:
        // 1.构造函数名称与类名相同
        // 2.没有返回值,不需要写void
        // 3.可以有参数,可以进行重载
        Test(int var) // 构造函数
        {
            m_var = var; // 初始化成员属性
        }
};

(2)析构函数

  • ++释放对象++
  • 不提供析造函数时,编译器会提供默认构造函数,函数实现是空的
  • 析构函数不需要调用,编译器会自动调用
cpp 复制代码
class Test
{
    private:
        int m_var;
    public:
        Test(int var) // 构造函数
        {
            m_var = var; // 初始化成员属性
        }
        // 1.析构函数名称与类名相同
        // 2.没有返回值,不需要写void
        // 3.不可以有参数,不可以进行重载
        ~Test()
        { /* 清理数据 */ }
};

2.2 构造函数分类与调用

(1)分类方法

  • 按**++参数++**分类:有参数构造、无参数构造

    cpp 复制代码
    class Test
    {
        public:
            int m_var;
        public:
            Test() // 无参构造函数(默认构造)
            {}
            Test(int var) // 有参构造函数
            {
                m_var = var; // 初始化成员属性
            }
    };
  • 按**++类型++** 分类:普通构造、拷贝构造

    cpp 复制代码
    class Test
    {
        public:
            int m_var;
        public:
            Test(const Test & test) // 拷贝构造函数(将传入的对象的属性拷贝到当前对象)
            {
                m_var= test.m_var;
            }
    };

(2)调用方式

  • 括号法

    cpp 复制代码
    int main()
    {
        int var = 10;
        Test test1;         // 无参构造调用,不要加()
        Test test2(10);     // 有参构造调用
        Test test3(test2);  // 拷贝构造调用
    }
  • 显示调用法

    cpp 复制代码
    int main()
    {
        Test test1;                 // 无参构造调用,不要加()
        Test test2 = Test(10);      // 有参构造调用
        Test test3 = Test(test2);   // 拷贝构造调用
        Test(10);   // 匿名对象,当前行执行完后,系统立即清除该匿名对象
    }
  • 隐式转换法

    cpp 复制代码
    int main()
    {
        Test test1 = 10;    // 有参构造调用
        Test test2 = test1; // 拷贝构造调用
    }

2.3 拷贝构造函数使用时机

  • 使用已创建 的对象初始化一个的对象
  • 值传递的方式将对象作为函数的参数进行传递
cpp 复制代码
void func(Test test)
{
    std::cout << test.m_var << std::endl;
}
​
int main()
{
    Test test1 = 10;    
    func(test1);    // 值传递方式传对象
}
  • 值返回的方式将一个局部对象在函数中进行返回
cpp 复制代码
Test func()
{
    Test test(10);
    return test;    // 返回局部对象
}
​
int main()
{
    Test test1(10); 
    func(test1);    // 值传递方式传对象
}

2.4 构造函数调用规则

(1)默认情况下,C++编译器会为一个类提供:

  • 默认构造函数-->无参,函数体空
  • 默认析构函数-->无参,函数体空
  • 默认拷贝构造函数-->类属性的值拷贝

(2)构造函数调用规则:

  • 如果自定义了有参构造函数,编译器将不再提供默认无参构造函数,但依然会提供默认拷贝构造函数
  • 如果自定义了**++拷贝构造函数++** ,编译器将不再提供**++其他构造函数++**

2.5 深拷贝和浅拷贝

(1)浅拷贝

  • 简单赋值拷贝操作,存在析构同一个堆指针两次的风险
cpp 复制代码
class Test
{
    public:
        int m_var;
        int *m_varp;
    public:
        Test(var, varp)
        {
            m_var = var;
            m_varp = new int(varp); // 从堆申请新的内存块
        }
        ~Test()
        {
            if(m_varp != NULL)
            {
                delete m_varp;  // 堆上开辟的内存需要主动消除
                m_varp = NULL;  // 避免出现野指针
            }
        }
};
​
int main()
{
    Test test(10, 20);
    Test test2(test);   // 调用默认拷贝构造函数(浅拷贝),仅拷贝指针变量,但两个指针指向
                        // 同一块内存,调用析构函数时,将会delete同一块堆内存两次,报错
}

(2)深拷贝

  • 堆区重新申请空间,进行拷贝操作
cpp 复制代码
class Test
{
    public:
        int m_var;
        int *m_varp;
    public:
        Test(var, varp)
        {
            m_var = var;
            m_varp = new int(varp); // 从堆申请新的内存块
        }
        Test(const Test & test) // 自行定义深拷贝构造函数
        {
            m_var = test.m_var;
            m_varp = new int(*(test.m_varp); // 重新开辟自己的堆内存块
        }
        ~Test()
        {
            if(m_varp != NULL)
            {
                delete m_varp;  // 堆上开辟的内存需要主动消除
                m_varp = NULL;  // 避免出现野指针
            }
        }
};

2.6 初始化列表

  • 类在构造函数中使用初始化列表**++给属性进行赋初值++**
cpp 复制代码
class Test
{
    public:
        int m_var;
        int m_var1;
    public:
        Test(int var, int var1):m_var(var),m_var1(var1)
        { /* 初始化列表给属性赋初值,构造函数体中不在做赋初值操作 */ }
};

2.7 类对象作为类成员

  • 当其他类对象作为本类成员时,++先构造其他类对象++,再构造本类对象
  • 析构函数与构造函数顺序相反
cpp 复制代码
class Phone
{
    public:
        std::string m_pName;
    
        Phone(std::string pName)
        {
            m_pName = pName
        }
}
​
class Person
{
    public:
        std::string m_name;
        Phone m_phone;
    
        // m_phone(pname)等价于Phone phone = pname
        // 即Phone有参构造函数的隐式调用
        Person(std::string name, std::string pname):m_name(name),m_phone(pname)
        {}
}

2.8 静态成员

  • 在成员变量或成员函数前,加上关键字"static",就变成了静态成员

(1)静态成员变量

  • 所有对象共享一份数据 --> 所有对象都可以修改
  • 在**++编译阶段++**分配内存
  • 类内声明,++类外初始化++
cpp 复制代码
class Test
{
    public:
        static int m_var;   // 静态成员变量(类内声明)
};
​
int Test::m_var = 100;  // 静态成员变量(类外初始化)
​
int main()
{
    // 静态成员变量的两种访问方式:
    // 1.对象访问 --> 对象.变量
    Test test;
    std::cout << test.m_var << std::endl;
    // 2.类名访问 --> 类名::变量
    std::cout << Test::m_var << std::endl;
}

(2)静态成员函数

  • 所有对象共享静态成员函数
  • 静态成员函数只能访问静态成员变量,不能访问非静态成员变量(++无法区分非静态成员变量属于哪个对象++)
cpp 复制代码
class Test
{
    public:
        static int m_var;
        static void func()  // 静态成员函数
        { m_var = 200; }    // 只能访问静态成员变量
};
​
int Test::m_var = 100;
​
int main()
{
    // 静态成员函数的两种访问方式:
    // 1.对象访问 --> 对象.函数
    Test test;
    test.func();
    // 2.类名访问 --> 类名::函数
    Test::func();
}
相关推荐
shykevin1 小时前
python开发Streamable HTTP MCP应用
开发语言·网络·python·网络协议·http
我不是程序猿儿1 小时前
【C#】 lock 关键字
java·开发语言·c#
漫路在线1 小时前
JS逆向-某易云音乐下载器
开发语言·javascript·爬虫·python
小辉懂编程2 小时前
C语言:51单片机实现数码管依次循环显示【1~F】课堂练习
c语言·开发语言·51单片机
Magnum Lehar4 小时前
3d游戏引擎EngineTest的系统实现3
java·开发语言·游戏引擎
Mcworld8574 小时前
java集合
java·开发语言·windows
成功人chen某4 小时前
配置VScodePython环境Python was not found;
开发语言·python
wuqingshun3141594 小时前
蓝桥杯 16. 外卖店优先级
c++·算法·职场和发展·蓝桥杯·深度优先
海绵宝宝贾克斯儿5 小时前
C++中如何实现一个单例模式?
开发语言·c++·单例模式