当前位置: 首页 > article >正文

【C++篇】深度解析类与对象(中)

引言

在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。

一、类的默认成员函数

在C++中,编译器会为每个类自动生成一些成员函数,即使你没有显式地编写这些函数。这些默认成员函数帮助我们快速完成一些常见的操作。通常情况下,一个类在没有显式定义某些函数时,编译器会为其自动生成六个默认成员函数(需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解⼀下即可):

1.默认构造函数:
当没有显式编写构造函数时,编译器会自动生成一个默认构造函数,用来初始化对象。
2.析构函数:
在没有定义析构函数时,编译器会自动生成一个析构函数,用来在对象被销毁时释放资源。
3.拷贝构造函数:
如果没有编写拷贝构造函数,编译器会生成一个默认的拷贝构造函数,用来通过已有对象创建新的对象。
4.赋值运算符重载:
当我们没有定义赋值运算符(=)时,编译器会生成一个默认的赋值运算符,用来将一个对象的值赋给另一个对象。
5.取地址运算符重载:
允许使用 & 来获取对象的地址,编译器会为每个类自动生成取地址运算符。
6.const 取地址运算符重载:
当对象是 const 时,编译器也会生成对应的取地址运算符。

接下来我们会具体讨论这些函数,了解它们的作用以及在什么情况下我们需要自行实现这些函数。

二、构造函数

2.1 构造函数的作用

构造函数是一个用于初始化对象的特殊成员函数。它的名字与类名相同,并且在创建对象时会被自动调用。构造函数的主要任务是确保对象在被创建时有一个明确的初始状态。可以将它理解为对象的"出生",从它开始,对象拥有了完整的、可用的状态。

构造函数的功能类似于我们在C语言中为结构体编写的初始化函数,但更为方便,因为它可以自动调用,而不需要每次手动去调用一个初始化函数。

2.2 构造函数的特点

1.函数名与类名相同:构造函数的名字必须和类名一致。

2.没有返回值:构造函数不需要返回类型,也不能有返回值。

3.自动调用:对象创建时,系统自动调用构造函数初始化对象。

4.支持重载:可以根据不同参数列表定义多个构造函数。

5.默认构造函数

  • 如果没有定义构造函数,编译器会自动生成一个无参的默认构造函数。

  • 一旦定义了任何构造函数,编译器就不会自动生成默认的无参构造函数。

6.默认构造的多种情况

  • 无参构造、全缺省构造(所有参数都有默认值)、编译器自动生成的构造都属于默认构造。

  • 这三者不能同时存在,因为都满足“可以不传实参调用”的条件。

7.初始化行为

  • 自动生成的构造函数对内置类型成员变量的初始化没有要求。

  • 自定义类型成员变量需要调用其默认构造函数初始化,否则需用初始化列表。

补充说明

  • 内置类型是指C++语言本身提供的基本数据类型,如 intchardouble 和指针等。
  • 自定义类型是指通过 classstruct 等关键字定义的类型。

2.3 构造函数的类型

C++中,构造函数可以有多个类型,主要包括:

  1. 无参构造函数:用于初始化一个对象,没有需要用户提供的参数。例如:

    class Date {
    public:
        Date() {
            _year = 1;
            _month = 1;
            _day = 1;
        }
    private:
        int _year;
        int _month;
        int _day;
    };
    

    在这个例子中,无参构造函数会将日期的年、月、日初始化为1。

  2. 带参构造函数:用于在创建对象时指定初始值。例如:

    class Date {
    public:
        Date(int year, int month, int day) {
            _year = year;
            _month = month;
            _day = day;
        }
    private:
        int _year;
        int _month;
        int _day;
    };
    

    这样用户在创建对象时,可以通过传递参数来指定对象的初始状态:Date d(2025, 5, 10);

  3. 全缺省构造函数:带有所有默认参数的构造函数,也可以作为无参构造函数使用。例如:

    class Date {
    public:
        Date(int year = 1, int month = 1, int day = 1) {
            _year = year;
            _month = month;
            _day = day;
        }
    private:
        int _year;
        int _month;
        int _day;
    };
    

    这种方式使得在创建对象时,既可以不传递参数,也可以只传递部分参数,从而提高了代码的灵活性。

2.4 初始化列表

什么是初始化列表?

初始化列表是构造函数的一种特殊语法,用于在对象创建时为其成员变量赋初值。它的语法是在构造函数的参数列表之后,冒号(:)后面跟随成员变量的初始化代码。初始化列表让成员变量在对象创建时直接被初始化,而不是先默认初始化再赋值。

class Point {
public:
    Point(int x, int y) : _x(x), _y(y) {} // 这里是初始化列表
private:
    int _x;
    int _y;
};

在这个例子中,Point 类有两个成员变量 _x_y。构造函数使用初始化列表将传入的参数 xy 直接赋值给 _x_y

为什么要用初始化列表?

  • 提高效率:初始化列表可以避免成员变量被先默认初始化再赋值,减少不必要的操作。
  • 必须使用的情况:对于常量(const)或引用(&)类型的成员变量,它们必须在对象创建时通过初始化列表进行赋值。

2.5 构造函数的调用时机

构造函数在以下几种情况下被调用:

  1. 定义对象时Date d1;,会调用无参构造函数。

  2. 通过参数列表创建对象Date d2(2025, 12, 25);,会调用带参构造函数。

  3. 在容器中创建对象时:例如,向std::vector中添加元素,容器会使用构造函数创建新对象。

三、析构函数

3.1 析构函数的作用

析构函数是用于销毁对象的特殊成员函数。它的名字是在类名前加上波浪号~,没有参数且没有返回值。析构函数的主要任务是释放对象在生命周期中占用的资源,例如动态分配的内存、打开的文件句柄等。

析构函数和构造函数形成了一个完整的生命周期管理机制,确保对象的创建和销毁过程一致性和安全性。

3.2 析构函数的特点

1.函数命名:析构函数的名字是在类名前加上 ~,例如,类 Stack 的析构函数为 ~Stack()

2.无参且无返回值:析构函数没有参数,也不需要返回类型。

3.自动调用:当对象超出其作用域或被显式删除(使用 delete)时,析构函数会被自动调用。

4.唯一性:一个类只能有一个析构函数。如果没有显式定义,系统会自动生成一个默认析构函数。

5.编译器生成的析构行为

  • 对内置类型成员不做处理。

  • 对自定义类型成员,编译器生成的析构函数会自动调用这些成员的析构函数。

6.显式定义的析构函数

  • 自定义类型成员的析构函数总会自动调用,无论析构函数是自动生成还是显式定义。

  • 如果类没有动态资源管理需求,可以使用编译器生成的默认析构函数。

7.手动编写析构函数的必要性

  • 当类中有动态资源(如堆内存)时,一定要自己编写析构函数来释放资源,否则会导致内存泄漏。

8.析构顺序:在局部作用域中,多个对象按定义的逆序进行析构(后定义的先析构)。

例如:

class Stack {
public:
    Stack(int n = 4) {
        _array = new int[n];
        _capacity = n;
        _top = 0;
    }
​
    ~Stack() {
        delete[] _array;
    }
​
private:
    int* _array;
    size_t _capacity;
    size_t _top;
};

在这个例子中,~Stack()析构函数用于释放构造函数中动态分配的内存。如果没有这个析构函数,当对象销毁时,动态分配的内存无法释放,就会导致内存泄漏。

3.3 析构函数的调用时机

析构函数在以下情况下会被调用:

  1. 对象离开作用域:例如,在main()函数中定义的局部对象在函数结束时会被自动销毁。

  2. 对象被显式删除:当通过delete销毁一个对象时,析构函数会被调用。

  3. 容器销毁其元素:当std::vector或其他容器销毁其持有的对象时,它们也会调用相应对象的析构函数。

3.4 析构函数的重要性

析构函数对于管理动态内存和其他系统资源非常重要。例如,如果类中包含指向堆内存的指针,而我们没有实现自定义的析构函数,则该指针所指向的内存不会被释放,从而导致内存泄漏。因此,任何涉及到动态内存分配的类,几乎都需要实现一个自定义的析构函数。

四、拷贝构造函数

4.1 拷贝构造函数的作用

拷贝构造函数用于通过已有对象创建新对象。拷贝构造函数的主要目的是使新对象具有与原对象相同的状态。

比如说,你有一个日历日期对象 Date,想要再创建一个新的 Date,内容和原来的日期一样,这时就需要用到拷贝构造函数。

#include <iostream>
using namespace std;

class Date {
public:
    // 普通构造函数,用年、月、日来创建日期对象
    Date(int year, int month, int day) : _year(year), _month(month), _day(day) {}

    // 拷贝构造函数,用一个已有的Date对象d来创建新的Date对象
    Date(const Date& d) : _year(d._year), _month(d._month), _day(d._day) {}

    // 打印日期的方法
    void Print() const {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

private:
    int _year;   // 年
    int _month;  // 月
    int _day;    // 日
};

int main() {
    Date date1(2024, 10, 21); // 用普通构造函数创建日期对象
    Date date2 = date1;       // 用拷贝构造函数创建一个新的Date对象,内容和date1一样

    // 打印两个日期对象
    date1.Print(); // 输出:2024/10/21
    date2.Print(); // 输出:2024/10/21

    return 0;
}

这里,拷贝构造函数Date(const Date& d)通过已有的对象d来初始化新的对象d2

4.2 拷贝构造函数的特点

1.构造函数重载:拷贝构造函数是构造函数的一种重载。

2.参数要求:第一个参数必须是类类型对象的引用,不能用传值方式,否则会引发无限递归。可以有多个参数,但第一个必须是引用,后面的参数要有默认值。

3.调用场合:拷贝构造在传值传参和传值返回时都会被调用。

4.默认生成:如果没有显式定义,编译器会生成默认的拷贝构造,对内置类型执行浅拷贝,对自定义类型调用其拷贝构造。

5.使用场景

  • 纯内置类型成员时,默认拷贝构造即可。

  • 有指向资源的成员时,需要自定义深拷贝。

  • 含自定义类型成员时,编译器自动调用成员的拷贝构造。

6.实现建议:如果类有析构函数处理资源,通常也需要自己写拷贝构造。

7.传值返回与引用

  • 传值返回会调用拷贝构造。

  • 传引用返回不会拷贝,但要确保返回对象在函数结束后仍存在。

4.3 拷贝构造函数的调用时机

拷贝构造函数通常在以下几种情况下调用:

  1. 对象按值传递时

    void Func(Date d) {
        d.Print();
    }
    Date d1(2024, 5, 12);
    Func(d1);  // 调用拷贝构造函数

    在将对象d1传递给函数Func时,d1按值传递,因此会调用拷贝构造函数。

  2. 对象按值返回时

    Date CreateDate() {
        Date d(2024, 5, 12);
        return d;  // 返回时调用拷贝构造函数
    }

    在函数返回对象时,会调用拷贝构造函数。

  3. 用已有对象初始化新对象时

    Date d1(2024, 5, 12);
    Date d2 = d1;  // 调用拷贝构造函数

4.4 浅拷贝与深拷贝

  • 浅拷贝:复制对象时,只复制指针的地址,新旧对象共享同一块内存。如果一个对象释放了这块内存,另一个对象就会出问题。

  • 深拷贝:为新对象分配独立的内存,并复制原对象的数据。这样新旧对象各自有自己的内存,不会互相影响。

示例代码:实现深拷贝

以下是一个 Stack 类的示例,实现了深拷贝:

#include <iostream>
#include <cstring> // 用于memcpy
using namespace std;

class Stack {
public:
    // 构造函数,初始化栈
    Stack(int n = 4) {
        _array = new int[n]; // 分配内存
        _capacity = n;
        _top = 0;
    }

    // 拷贝构造函数,实现深拷贝
    Stack(const Stack& other) {
        _array = new int[other._capacity]; // 分配新内存
        _capacity = other._capacity;
        _top = other._top;
        memcpy(_array, other._array, sizeof(int) * _top); // 复制数据
    }

    // 析构函数,释放内存
    ~Stack() {
        delete[] _array;
    }

private:
    int* _array;     // 指向栈的数组
    size_t _capacity; // 栈的容量
    size_t _top;      // 栈顶位置
};

代码要点

构造函数 Stack(int n = 4)

  • 初始化一个栈对象,默认容量为4。使用 new 分配内存来存放整数数组,_capacity 用来存储栈的容量,_top 表示栈顶的位置(初始为0)。

拷贝构造函数 Stack(const Stack& other)

  • 深拷贝实现:当用一个已有的 Stack 对象创建新的 Stack 对象时,这个构造函数会被调用。首先,为新对象分配一块和原对象 _capacity 大小相同的内存。然后,将原对象的 _capacity_top 的值复制给新对象。使用 memcpy 函数,将原对象 _array 中的数据复制到新对象的 _array 中。这一步是深拷贝的关键,因为它确保了新对象和原对象有独立的内存空间。

析构函数 ~Stack()

  • 释放分配的内存,防止内存泄漏。对于每个 Stack 对象,析构函数在对象生命周期结束时自动调用。

为什么要用深拷贝?

当类中包含指针成员(如动态分配的内存)时,必须使用深拷贝,否则会出现多个对象共享同一块内存的情况。这可能导致程序出错或崩溃,特别是在析构时释放内存时。如果类只包含内置类型成员(如 intdouble),那么默认的浅拷贝就足够了。

五、赋值运算符重载

5.1 运算符重载

C++支持运算符重载,使得自定义类型可以像内置类型一样使用运算符。例如,可以为自定义类重载+-=等运算符,使这些类对象能与内置类型有类似的操作体验。运算符重载的目的是提高代码的可读性和简洁性,让代码更自然地表达程序的意图。

5.2 赋值运算符重载

默认情况下,C++对对象进行赋值时,编译器会执行“浅拷贝”,即按成员逐个复制。这在类中仅包含内置类型成员时没问题,但如果类中有指针成员,浅拷贝会导致两个对象共享同一块内存资源,可能会引发内存管理问题,例如重复释放同一块内存。因此,针对有动态内存分配的类,我们需要重载赋值运算符,以实现“深拷贝”。

赋值运算符重载的实现示例

#include <cstring> // 为了使用memcpy函数

class Stack {
public:
    // 构造函数,初始化栈,默认容量为4
    Stack(int n = 4) {
        _array = new int[n]; // 为栈分配内存
        _capacity = n;       // 设置栈的容量
        _top = 0;            // 初始化栈顶位置为0,表示栈为空
    }

    // 赋值运算符重载,用于将一个已有的Stack对象赋值给另一个Stack对象
    Stack& operator=(const Stack& other) {
        if (this != &other) {  // 检查是否自赋值(避免自己给自己赋值)
            delete[] _array;  // 释放已有的内存资源,防止内存泄漏
            _array = new int[other._capacity]; // 分配新的内存空间
            _capacity = other._capacity; // 复制原对象的容量
            _top = other._top;           // 复制原对象的栈顶位置
            memcpy(_array, other._array, sizeof(int) * _top); // 复制原对象的数据
        }
        return *this; // 返回当前对象的引用,以支持链式赋值
    }

    // 析构函数,释放栈的内存资源
    ~Stack() {
        delete[] _array; // 释放分配的内存,防止内存泄漏
    }

private:
    int* _array;     // 动态数组,用于存储栈的元素
    size_t _capacity; // 栈的最大容量
    size_t _top;      // 当前栈顶位置(表示栈中的元素个数)
};

在这个示例中,重载了赋值运算符以确保在赋值时正确处理动态内存,并避免内存泄漏或重复释放的错误。自赋值检查 (if (this != &other)) 可以避免在赋值给自己时发生内存问题。

5.3 日期类实现

Date.h

#pragma once  
#include<iostream>
#include<stdbool.h>
#include<assert.h>
using namespace std;

class Date
{
    // 友元声明,使得非成员函数可以访问私有成员
    friend ostream& operator<<(ostream& out, const Date& d); // 重载输出运算符 <<
    friend istream& operator>>(istream& in, Date& d);        // 重载输入运算符 >>

public:
    // 构造函数,提供默认参数用于初始化年、月、日
    Date(int year = 2000, int month = 1, int day = 1);  // 构造函数声明,默认值在这里给出

    // 打印日期
    void Print();

    // 检查日期是否合法
    bool CheckDate() {
        if (_month < 1 || _month > 12 || _day < 1 || _day > GetMonthDay(_year, _month)) {
            return false;  // 如果月份或日期超出合理范围,返回false
        } else {
            return true;   // 日期有效,返回true
        }
    }

    // 获取指定月份的天数
    int GetMonthDay(int year, int month) {
        // 定义每个月份的天数,数组下标从1开始,0元素为-1占位
        static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        // 判断是否为闰年,且月份为2月,如果是,返回29天
        if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
            return 29;
        }
        return monthDayArray[month]; // 否则返回对应月份的天数
    }

    // 比较运算符重载,比较日期大小
    bool operator<(const Date& d);
    bool operator>(const Date& d);
    bool operator<=(const Date& d);
    bool operator>=(const Date& d);
    bool operator==(const Date& d);
    bool operator!=(const Date& d);

    // 日期增加指定天数,影响当前对象
    Date& operator+=(int day);

    // 日期增加指定天数,返回新的日期对象
    Date operator+(int day);

    // 日期减少指定天数,影响当前对象
    Date& operator-=(int day);

    // 日期减少指定天数,返回新的日期对象
    Date operator-(int day);

    // 前置自增运算符,增加一天,返回增加后的引用
    Date& operator++();

    // 后置自增运算符,增加一天,返回增加前的对象(使用int区分后置)
    Date operator++(int);

    // 前置自减运算符,减少一天,返回减少后的引用
    Date& operator--();

    // 后置自减运算符,减少一天,返回减少前的对象(使用int区分后置)
    Date operator--(int);

    // 计算两个日期之间的差距,返回天数
    int operator-(const Date& d); 

private:
    int _year;   // 年
    int _month;  // 月
    int _day;    // 日
};

// 重载输出运算符,用于输出日期对象
ostream& operator<<(ostream& out, const Date& d);

// 重载输入运算符,用于输入日期对象
istream& operator>>(istream& in, Date& d);

Date.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include "Date.h"

// 构造函数 - 初始化年、月、日
Date::Date(int year, int month, int day) {
    _year = year;
    _month = month;
    _day = day;
    
    // 检查日期是否有效,如果无效则输出提示信息
    if (!CheckDate()) {
        cout << "日期非法->";
        cout << *this; // 输出当前日期对象
    }
}

// 打印日期
void Date::Print() {
    cout << _year << "/" << _month << "/" << _day << endl;
}

// 重载小于运算符 - 比较日期大小
bool Date::operator<(const Date& d) {
    if (_year < d._year) {
        return true; // 当前年份小于比较对象
    } else if (_year == d._year && _month < d._month) {
        return true; // 年相等,当前月份小于比较对象
    } else if (_year == d._year && _month == d._month && _day < d._day) {
        return true; // 年月相等,当前日期小于比较对象
    }
    return false; // 不满足以上条件,返回false
}

// 重载大于运算符 - 实现为取小于运算符的否定
bool Date::operator>(const Date& d) {
    return !(*this < d);
}

// 重载小于等于运算符
bool Date::operator<=(const Date& d) {
    return *this < d || *this == d; // 小于或等于则返回true
}

// 重载大于等于运算符
bool Date::operator>=(const Date& d) {
    return !(*this < d); // 取小于的反面
}

// 重载等于运算符 - 检查年、月、日是否均相等
bool Date::operator==(const Date& d) {
    return _year == d._year && _month == d._month && _day == d._day;
}

// 重载不等于运算符
bool Date::operator!=(const Date& d) {
    return !(*this == d);
}

// 重载+=运算符 - 日期加上指定天数
Date& Date::operator+=(int day) {
    if (day < 0) {
        return *this -= -day; // 如果天数为负数,则调用-=
    }

    _day += day; // 增加天数
    while (_day > GetMonthDay(_year, _month)) {
        _day -= GetMonthDay(_year, _month); // 减去当前月份的天数
        ++_month; // 进入下一个月
        if (_month == 13) {
            _year++;  // 如果月份是13,进入下一年
            _month = 1; // 月份重置为1
        }
    }
    return *this; // 返回当前对象的引用
}

// 重载+运算符 - 返回新的日期对象
Date Date::operator+(int day) {
    Date tmp(*this); // 创建副本,避免修改当前对象
    tmp += day; // 使用+=实现
    return tmp;
}

// 重载-=运算符 - 日期减去指定天数
Date& Date::operator-=(int day) {
    if (day < 0) {
        return *this += -day; // 如果天数为负数,则调用+=
    }

    _day -= day; // 减去天数
    while (_day <= 0) {
        --_month; // 进入上一个月
        if (_month == 0) {
            _year--; // 如果月份是0,进入上一年
            _month = 12; // 月份设为12
        }
        _day += GetMonthDay(_year, _month); // 补足当前月的天数
    }
    return *this; // 返回当前对象的引用
}

// 重载-运算符 - 返回新的日期对象,日期减去指定天数
Date Date::operator-(int day) {
    Date tmp(*this); // 创建副本
    tmp -= day; // 使用-=实现
    return tmp;
}

// 重载前置++运算符 - 日期加1天,返回自身
Date& Date::operator++() {
    *this += 1;
    return *this;
}

// 重载后置++运算符 - 日期加1天,返回旧值
Date Date::operator++(int) {
    Date tmp(*this); // 创建副本,保存当前状态
    *this += 1;      // 增加1天
    return tmp;      // 返回旧的对象
}

// 重载前置--运算符 - 日期减1天,返回自身
Date& Date::operator--() {
    *this -= 1;
    return *this;
}

// 重载后置--运算符 - 日期减1天,返回旧值
Date Date::operator--(int) {
    Date tmp(*this); // 创建副本,保存当前状态
    *this -= 1;      // 减少1天
    return tmp;      // 返回旧的对象
}

// 重载-运算符 - 返回两个日期之间的天数差
int Date::operator-(const Date& d) {
    Date max = *this; // 假定当前日期为较大者
    Date min = d;
    int flag = 1; // 标志符号,表示方向
    if (*this < d) {
        max = d;
        min = *this;
        flag = -1; // 如果当前日期小于比较日期,调整符号
    }

    int n = 0; // 用于计数两个日期之间的天数
    while (min != max) {
        ++min; // 将较小的日期逐步增加
        ++n;   // 计数增加
    }
    return n * flag; // 返回带有方向的天数差
}

// 重载输出运算符 - 输出日期信息
ostream& operator<<(ostream& out, const Date& d) {
    out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
    return out;
}

// 重载输入运算符 - 输入日期信息
istream& operator>>(istream& in, Date& d) {
    while (1) {
        cout << "请依次输入年月日:>";
        in >> d._year >> d._month >> d._day;
        if (d.CheckDate()) {
            break; // 如果日期有效,退出循环
        } else {
            cout << "日期非法,请重新输入"; // 如果无效,要求重新输入
        }
    }
    return in;
}

test.cpp 

#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include"Date.h"
using namespace std;
int main()
{
	Date d1(2024, 10, 21);
	d1.Print();
	//Date d2 = d1 + (-100);
	//d2.Print();
	//d1 += -100;
	//d1.Print();
	//++d1;
	//d1.Print();
	//d1++;
	//d1.Print();
	Date d2(2024, 12, 16);
	//cout << d1 - d2 << endl;
	cout << d2 - d1 << endl;
	cout << d1;
	operator<<(cout, d1);
	cin >> d1>>d2;
	cout << d1 << d2 << endl; 
	return 0;
}

六、取地址运算符重载

6.1 const 成员函数

const 成员函数用于保证函数内部不能修改对象成员变量。例如:

class Date {
public:
    Date(int year = 1, int month = 1, int day = 1) : _year(year), _month(month), _day(day) {}

    void Print() const {  // 该函数不能修改对象的状态
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

6.2 取地址运算符重载

可以重载取地址运算符 & 来控制取对象地址的行为,例如不希望外部获取对象的地址: 

class Date {
public:
    Date* operator&() {
        return nullptr; // 返回空指针,隐藏真实地址
    }

    const Date* operator&() const {
        return nullptr;
    }

private:
    int _year;
    int _month;
    int _day;
};

通过这篇博客,我们讨论了C++类的默认成员函数、构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符的重载。理解这些概念是学习C++面向对象编程的基础,也是管理内存、资源安全的关键。

如果有任何疑问,欢迎在评论区留言交流!


http://www.kler.cn/news/360346.html

相关文章:

  • ASP.NET.Web应用程序(.NET Framework)添加Swagger本地Debuge成功打开接口展示界面,发布服务器无法打开接口展示界面
  • 【ChatGPT】如何限定 ChatGPT 的回答范围
  • 网络资源模板--Android Studio 实现背单词App
  • 计算机毕业设计 基于Python的社交音乐分享平台的设计与实现 Python毕业设计 Python毕业设计选题【附源码+安装调试】
  • 【网络安全】IDOR与JWT令牌破解相结合,实现编辑、查看和删除数万帐户
  • 每天5分钟玩转C#/.NET之了解C#中的顶级语句
  • Go语言依赖注入方式
  • 高效容器化技术(2)---docker的安装
  • 实用好助手
  • 【NodeJS】NodeJS+mongoDB在线版开发简单RestfulAPI (一):项目简介及安装依赖
  • SpringBoot和Vue的图片上传的解决方案
  • Java爬虫API:获取商品详情数据的利器
  • 基于SpringBoot+Vue+uniapp微信小程序的教学质量评价系统的详细设计和实现
  • 构建可扩展、安全和智能的数字化解决方案:微服务架构与物联网的深度融合
  • gitlab项目转移群组
  • 汇编实现逆序复制数据
  • 物联网防爆气象站的工作原理
  • C07.L10.STL之队列
  • 【推导过程】常用离散分布的数学期望、方差、特征函数
  • 物流行业创新:SpringBoot技术应用