Javer 学 c++(十六):对象特性篇(上)

主要讲了 c++ 中对象的一些特性:构造函数、析构函数、拷贝、初始化以及指针等

1. 对象的初始化和清理

对象的初始化和清理是两个非常重要的问题。

一个对象或者变量没有初始状态,则对其使用后果是未知;

同样使用完一个对象或者变量后,没有及时清理,也会造成一定的安全问题。

c++使用 构造函数析构函数 来完成以上两个步骤,上面两个函数会被编译器自动调用,用来完成对象的初始化以及清理。这是编译器强制要求我们执行的步骤,如果开发人员不实现这两个函数的话,编译器会自动调用默认的空实现。

析构函数

主要作用于对象销毁之前,执行一些清理工作,系统自动调用

语法:~类名(){}

1.1 构造函数

主要作用于创建对象时为对象的成员变量赋初始值,构造函数由编译器自动调用,无需手动调用

语法:类名(){}

  • 按参数分:有参构造和无参构造

  • 按类型分:普通构造和拷贝构造

  • 三种调用方式:括号法、显示法、隐式转换法

c++ 复制代码
#include <iostream>
using namespace std;

class Person {
    int  age;
    string name;
    string sex;

public:
    // 有参构造
    Person(int age, string name, string sex) {
        this->age = age;
        this->name = name;
        this->sex = sex;
    }
    // 无参构造
    Person() {
        cout << "调用无参构造函数" << endl;
    }
    // 拷贝构造函数:按传进来的拷贝一份
    // 1. const防止修改 2. 使用引用
    Person(const Person &p) {
        age = p.age;
        name = p.name;
        sex = p.sex;
    }
};


int main() {
    // 调用方式一:括号法
    Person p1; // 调用无参构造,默认构造不用加括号(加了会被认为函数)
    Person p2(18, "张三", "男");// 调用有参构造
    Person p3(p2); // 拷贝构造

    // 调用方式二:显示法
    Person p4;
    Person p5 = Person(18, "张三", "男");
    Person p6 = Person(p5);

    Person(18, "张三", "男"); // 匿名对象

    // 调用方式三:隐式转换法
    Person p7 = p6;
    

    return 0;
}

1.2 拷贝构造调用时机

c++ 复制代码
#include <iostream>
using namespace std;

class Person {
    int  age;
    string name;
    string sex;

public:
    // 有参构造
    Person(int age, string name, string sex) {
        this->age = age;
        this->name = name;
        this->sex = sex;
    }
    // 无参构造
    Person() {
        cout << "调用无参构造函数" << endl;
    }
    // 拷贝构造函数:按传进来的拷贝一份
    // 1. const防止修改 2. 使用引用
    Person(const Person &p) {
        cout << "调用拷贝构造函数" << endl;
        age = p.age;
        name = p.name;
        sex = p.sex;
    }
};

// 拷贝构造场景一:使用一个对象初始化另一个对象
void test01() {
    Person p1(18, "张三", "男");
    Person p2(p1);
}

// 拷贝构造场景二:函数的参数是类对象
void test02(Person p) {

}

// 拷贝构造场景三:返回值是类对象
Person test03() {
    Person p(18, "张三", "男");
    cout << &p << endl;
    return p;
}


int main() {
    test01();

    Person p1(18, "张三", "男");
    test02(p1);

    Person p2 = test03();
    // test03里面的 p 地址和 p2地址不同,已经拷贝构造了

    // 我擦但是是发现是相同的,查了下是现代 c++ 的优化,如果检测到了会返回 p
    // 直接在 main 函数的栈里构造了 p,也就是 test03结束后 p 其实根本没有回收,继续在 main 函数中使用
    cout << &p2 << endl;

    return 0;
}

1.3 构造函数调用规则

默认情况下,c++编译器至少给一个类添加三个函数:

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝

构造函数的调用规则如下:

  • 如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数

1.4 深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作

沈拷贝:在堆区重新申请空间,进行赋值操作

c++ 复制代码
#include <iostream>
using namespace std;

class Person {
public:
    int  m_age;
    int *m_height;
    
    // 有参构造
    Person(int age, int height) {
        cout << "调用有参构造函数" << endl;
        m_age = age;
        m_height = new int(height);
    }
    // 无参构造
    Person() {
        cout << "调用无参构造函数" << endl;
    }

    // 拷贝构造
    Person(const Person &p) {
        cout << "调用拷贝构造函数" << endl;
        m_age = p.m_age;
        m_height = new int(*p.m_height);
    }

    // 析构函数
    ~Person() {
        // 应该释放掉在堆区开辟的数据
        if (m_height != nullptr) {
            delete m_height;
            m_height = nullptr;
        }
        cout << "调用析构函数" << endl;
    }
};

int main() {

    Person p1(10, 160);
    cout << "p1的年龄为:" << p1.m_age << "身高为:" << *p1.m_height << endl;

    // 报错:由于 p2由 p1拷贝构造,所以两个对象的 m_height 指针指向同一个地址,析构的时候会释放两次
    Person p2(p1);
    cout << "p2的年龄为:" << p2.m_age << "身高为:" << *p2.m_height << endl;

    // 利用深拷贝来解决:重写拷贝构造
    Person p3 = p1;
    cout << "p3的年龄为:" << p3.m_age << "身高为:" << *p3.m_height << endl;

    return 0;
}

初始化列表

c++提供了初始化列表语法,用来初始化属性

语法:

c 复制代码
#include <iostream>
using namespace std;

class Person {
public:
   int  m_age;
   int m_height;
   string m_name;

   // 正常有参构造
   Person(int age, int height, string name) {
       m_age = age;
       m_height = height;
       m_name = name;
   }

   // 初始化列表
   Person():m_age(0), m_height(0), m_name("") {
   }

   // 优势在于初始化的直接赋值,不用再赋值
   Person(int age, int height): m_age(age), m_height(height) {
       m_age = age;
   }

};

int main() {

   Person p1(10, 160, "张三");
   cout << "p1的年龄为:" << p1.m_age << ",身高为:" << p1.m_height << "姓名为:" << p1.m_name << endl;

   Person p2;
   cout << "p2的年龄为:" << p2.m_age << ",身高为:" << p2.m_height << "姓名为:" << p2.m_name << endl;

   Person p3(10, 160);
   cout << "p3的年龄为:" << p3.m_age << ",身高为:" << p3.m_height << endl;

   return 0;
}
相关推荐
十五年专注C++开发17 小时前
Qt-Nice-Frameless-Window: 一个跨平台无边框窗口(Frameless Window)解决方案
开发语言·c++·qt
鬼火儿17 小时前
Golang笔记——Interface类型
java·后端
三十_A17 小时前
【无标题】
前端·后端·node.js
蓝-萧17 小时前
Redis——Windows安装
java·后端
十子木18 小时前
C++ 类似pytorch的库,工具包,或者机器学习的生态
c++·pytorch·机器学习
王元_SmallA18 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
Moonbit18 小时前
招募进行时 | MoonBit AI : 程序语言 & 大模型
前端·后端·面试
AlianNiew18 小时前
从源码到实战:用 Java 打造“限时+防重放”的文件安全预览链接
java·后端
小威要向诸佬学习呀19 小时前
2025年软件外包避坑指南与平台推荐:开发者实用经验分享
后端
冒泡的肥皂19 小时前
2PL+MVCC看一些场景
数据库·后端·mysql