Lesson03---类与对象(中篇)

目录

[一、类的 6 个默认成员函数(总览)](#一、类的 6 个默认成员函数(总览))

[1.1 通俗概念](#1.1 通俗概念)

[1.2 6 个默认成员函数功能表](#1.2 6 个默认成员函数功能表)

[1.3 注意点](#1.3 注意点)

二、构造函数

[2.1 通俗概念](#2.1 通俗概念)

[2.2 关键注意点](#2.2 关键注意点)

三、析构函数

[3.1 通俗概念](#3.1 通俗概念)

[3.2 关键注意点](#3.2 关键注意点)

四、拷贝构造函数

[4.1 通俗概念](#4.1 通俗概念)

[4.2 关键注意点](#4.2 关键注意点)

五、赋值运算符重载

[5.1 通俗概念](#5.1 通俗概念)

[5.2 前置 ++ 和后置 ++ 重载(日期类实战)](#5.2 前置 ++ 和后置 ++ 重载(日期类实战))

通俗概念

关键注意点

[5.3 关键注意点](#5.3 关键注意点)

[六、const 成员函数](#六、const 成员函数)

[6.1 通俗概念](#6.1 通俗概念)

[6.2 关键注意点](#6.2 关键注意点)

[七、取地址及 const 取地址重载](#七、取地址及 const 取地址重载)

[7.1 通俗概念](#7.1 通俗概念)

[7.2 关键注意点](#7.2 关键注意点)

八、日期类完整实现(全功能实战)

[8.1 核心功能](#8.1 核心功能)

[8.2 关键注意点](#8.2 关键注意点)

九、学习总结与建议


一、类的 6 个默认成员函数(总览)

1.1 通俗概念

空类并不是真的 "空",编译器会自动生成 6 个默认成员函数 ------ 它们是对象 "出生、复制、赋值、销毁" 的默认行为,用户没显式写时,编译器自动补全。

1.2 6 个默认成员函数功能表

|-------------|---------------|---------------------------|
| 函数类型 | 核心功能 | 调用时机 |
| 构造函数 | 初始化对象 | 对象创建时(自动调用) |
| 析构函数 | 清理对象资源 | 对象销毁时(自动调用) |
| 拷贝构造函数 | 用已有对象创建新对象 | 新对象基于旧对象初始化(如Date d2(d1)) |
| 赋值运算符重载 | 把一个对象赋值给另一个对象 | 已有对象间赋值(如d1 = d2) |
| 取地址重载 | 获取对象地址 | 调用&对象时(默认生成即可) |
| const 取地址重载 | 获取 const 对象地址 | 调用&const对象时(默认生成即可) |

1.3 注意点

  • 仅当用户未显式实现时,编译器才生成默认版本;
  • 默认版本的核心逻辑:对内置类型(int/char 等)"浅拷贝 / 不初始化",对自定义类型(class/struct)调用其对应默认成员函数;
  • 涉及资源申请(如 malloc/new)的类,必须显式实现构造、析构、拷贝构造、赋值重载,否则会内存泄漏或崩溃。

二、构造函数

2.1 通俗概念

对象创建时自动调用的函数,核心任务是初始化对象 (不是开空间创建对象),就像婴儿出生时自动登记信息,不用手动操作。

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

class Date {
public:
    // 1. 无参构造函数(默认构造函数之一)
    Date() {
        _year = 1900;
        _month = 1;
        _day = 1;
    }

    // 2. 带参构造函数
    Date(int year, int month, int day) {
        _year = year;
        _month = month;
        _day = day;
    }

    // 3. 全缺省构造函数(默认构造函数之一)
    // Date(int year = 1900, int month = 1, int day = 1) {
    //     _year = year;
    //     _month = month;
    //     _day = day;
    // }

    void Print() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    // C++11特性:内置类型声明时可给默认值
    int _year = 0;
    int _month = 0;
    int _day = 0;
};

int main() {
    // 调用无参构造函数(正确)
    Date d1;
    d1.Print(); // 输出:1900-1-1

    // 调用带参构造函数(正确)
    Date d2(2024, 5, 22);
    d2.Print(); // 输出:2024-5-22

    // 错误示例:这是函数声明(返回Date类型,无参),不是对象创建
    // Date d3(); // 编译警告:未调用原型函数(误当变量定义)

    return 0;
}
cpp 复制代码
1900-1-1
2024-5-22

2.2 关键注意点

  1. 默认构造函数的三种形式 :无参构造、全缺省构造、编译器自动生成的构造函数,且默认构造函数只能有一个(注释掉无参构造才能用全缺省,否则编译报错);
  2. 编译器生成的默认构造函数:
    • 对内置类型(int/char 等):C++98 不初始化(随机值),C++11 支持声明时给默认值(如int _year = 0);
    • 对自定义类型(如 Time 类):会调用其默认构造函数;
  3. 构造函数无返回值函数名与类名相同支持重载
  4. 若用户显式实现任何构造函数(哪怕是带参的),编译器不再生成默认无参构造函数。

面试小提示

常考:"什么是默认构造函数?"------ 答:无参构造、全缺省构造、编译器默认生成的构造函数,三者只能存在一个。


三、析构函数

3.1 通俗概念

对象生命周期结束时自动调用的函数,核心任务是清理资源(如 malloc 的内存、打开的文件),就像人去世前清理个人物品,对象销毁前释放申请的资源。

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

typedef int DataType;
class Stack {
public:
    // 构造函数:申请资源
    Stack(size_t capacity = 3) {
        _array = (DataType*)malloc(sizeof(DataType) * capacity);
        if (nullptr == _array) {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = capacity;
        _size = 0;
        cout << "Stack():申请内存成功" << endl;
    }

    // 析构函数:清理资源(必须显式实现,否则内存泄漏)
    ~Stack() {
        if (_array) {
            free(_array);
            _array = nullptr; // 避免野指针
            _capacity = 0;
            _size = 0;
            cout << "~Stack():释放内存成功" << endl;
        }
    }

    void Push(DataType data) {
        // 简化版:未实现扩容
        if (_size < _capacity) {
            _array[_size++] = data;
        }
    }

private:
    DataType* _array; // 动态申请的内存(需清理)
    size_t _capacity;
    size_t _size;
};

void TestStack() {
    Stack s;
    s.Push(1);
    s.Push(2);
    // 函数结束时,s生命周期结束,自动调用析构函数
}

int main() {
    TestStack();
    return 0;
}
cpp 复制代码
Stack():申请内存成功
~Stack():释放内存成功

3.2 关键注意点

  1. 析构函数无参数、无返回值函数名是类名前加~不能重载(一个类只有一个析构函数);
  2. 编译器生成的默认析构函数:
    • 对内置类型:不做任何处理(如 int/char,无需清理);
    • 对自定义类型:调用其析构函数(如 Date 类中的 Time 成员,会自动调用~Time ());
  3. 若类中无资源申请(如 Date 类),可不用显式实现析构函数;若有资源申请(如 Stack 类的 malloc),必须显式实现,否则内存泄漏。

面试小提示

常考:"析构函数的作用是什么?什么时候需要显式实现?"------ 答:清理对象资源;类中涉及动态内存申请、文件打开等资源时,必须显式实现。


四、拷贝构造函数

4.1 通俗概念

用一个已存在的对象创建新对象时自动调用的函数,核心任务是复制对象的所有成员,就像用双胞胎哥哥的信息创建弟弟的身份档案。

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

class Date {
public:
    // 构造函数
    Date(int year = 1900, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date(int, int, int):" << this << endl;
    }

    // 拷贝构造函数:参数必须是const引用(避免无穷递归)
    Date(const Date& d) {
        _year = d._year;
        _month = d._month;
        _day = d._day;
        cout << "Date(const Date&):" << this << endl;
    }

    void Print() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

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

// 错误示例:拷贝构造函数参数为传值(编译报错,无穷递归)
// class Date {
// public:
//     Date(Date d) { // 错误:传值会触发拷贝构造,无限循环
//         _year = d._year;
//     }
// };

// 拷贝构造调用场景1:用已有对象创建新对象
void Test1() {
    Date d1(2024, 5, 22);
    Date d2(d1); // 调用拷贝构造
    d2.Print(); // 输出:2024-5-22
}

// 拷贝构造调用场景2:函数参数为类类型对象
void Test2(Date d) { // 传值时调用拷贝构造
    d.Print();
}

// 拷贝构造调用场景3:函数返回值为类类型对象
Date Test3() {
    Date d(2024, 5, 23);
    return d; // 返回时调用拷贝构造(编译器可能优化)
}

int main() {
    Test1();
    cout << "----------------" << endl;
    Date d4(2024, 5, 24);
    Test2(d4);
    cout << "----------------" << endl;
    Date d5 = Test3();
    return 0;
}
cpp 复制代码
Date(int, int, int):0x7ffee3b558a0
Date(const Date&):0x7ffee3b55890
2024-5-22
----------------
Date(int, int, int):0x7ffee3b55880
Date(const Date&):0x7ffee3b55850
2024-5-24
----------------
Date(int, int, int):0x7ffee3b55840
Date(const Date&):0x7ffee3b55870

4.2 关键注意点

  1. 拷贝构造函数是构造函数的重载 ,参数必须是const 类名&
    • 用引用是为了避免传值触发拷贝构造,导致无穷递归;
    • 用 const 是为了保护原对象,避免被修改;
  2. 编译器生成的默认拷贝构造函数:
    • 对内置类型:按字节浅拷贝(值拷贝);
    • 对自定义类型:调用其拷贝构造函数;
  3. 浅拷贝的问题:若类中有资源申请(如 Stack 类),浅拷贝会导致两个对象共用同一块内存,析构时重复释放(崩溃),需显式实现深拷贝;
  4. 三大调用场景:用旧对象创建新对象、函数参数为类类型(传值)、函数返回值为类类型(传值)。

面试小提示

常考:"拷贝构造函数为什么参数必须是引用?"------ 答:若为传值,会触发拷贝构造函数本身,导致无限递归调用,编译报错。


五、赋值运算符重载

5.1 通俗概念

已有对象间赋值时调用的函数(如d1 = d2),核心任务是把一个对象的成员赋值给另一个对象,区别于拷贝构造(拷贝构造是 "创建新对象",赋值是 "已有对象赋值")。

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

class Date {
public:
    // 构造函数
    Date(int year = 1900, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
    }

    // 拷贝构造函数
    Date(const Date& d) {
        _year = d._year;
        _month = d._month;
        _day = d._day;
        cout << "拷贝构造" << endl;
    }

    // 赋值运算符重载(正确格式)
    Date& operator=(const Date& d) {
        // 1. 检测自赋值(避免自己给自己赋值,浪费资源)
        if (this != &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        // 2. 返回*this(支持连续赋值,如d1 = d2 = d3)
        return *this;
    }

    void Print() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

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

// 错误示例:赋值运算符重载成全局函数(编译报错)
// Date& operator=(Date& left, const Date& right) {
//     if (&left != &right) {
//         left._year = right._year;
//     }
//     return left;
// }

int main() {
    Date d1(2024, 5, 22), d2(2024, 5, 23), d3;
    // 连续赋值(支持,因为返回Date&)
    d3 = d2 = d1;
    d3.Print(); // 输出:2024-5-22

    // 赋值 vs 拷贝构造
    Date d4 = d1; // 拷贝构造(创建d4时初始化)
    Date d5;
    d5 = d1; // 赋值运算符重载(d5已存在)
    return 0;
}
cpp 复制代码
拷贝构造
2024-5-22

5.2 前置 ++ 和后置 ++ 重载(日期类实战)

通俗概念

  • 前置 ++:先自增,再返回自增后的对象(如++d,返回 d+1 后的值);
  • 后置 ++:先返回当前对象,再自增(如d++,返回 d 原来的值,之后 d+1)。
cpp 复制代码
#include <iostream>
using namespace std;

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

    // 前置++:返回引用(对象未销毁,直接修改)
    Date& operator++() {
        _day += 1;
        // 这里简化处理,未考虑月份/年份进位(完整逻辑见日期类实现)
        return *this;
    }

    // 后置++:返回值(需保存旧值,临时对象不能返回引用)
    // 加int参数是占位符,编译器自动传递,区分前置/后置
    Date operator++(int) {
        Date temp(*this); // 保存当前对象(旧值)
        _day += 1; // 自增
        // 简化处理,未考虑进位
        return temp; // 返回旧值
    }

    void Print() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

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

int main() {
    Date d(2024, 5, 22);

    // 后置++:先使用,后自增
    Date d1 = d++;
    cout << "d1(d++后):";
    d1.Print(); // 输出:2024-5-22(旧值)
    cout << "d(d++后):";
    d.Print(); // 输出:2024-5-23(自增后)

    // 前置++:先自增,后使用
    Date d2 = ++d;
    cout << "d2(++d后):";
    d2.Print(); // 输出:2024-5-24(自增后)
    cout << "d(++d后):";
    d.Print(); // 输出:2024-5-24(自增后)

    return 0;
}
cpp 复制代码
d1(d++后):2024-5-22
d(d++后):2024-5-23
d2(++d后):2024-5-24
d(++d后):2024-5-24

关键注意点

  1. 前置 ++:
    • 无占位参数,返回Date&(当前对象未销毁,直接返回引用,提高效率);
    • 先修改对象,再返回修改后的对象;
  2. 后置 ++:
    • 必须加int占位参数(无实际意义,仅用于编译器区分);
    • 返回Date(值返回),因为需要保存旧值(临时对象),不能返回引用;
  3. 完整实现需处理日期进位(如 5 月 31 日 ++ 后是 6 月 1 日),后续日期类会补充。

面试小提示

常考:"前置 ++ 和后置 ++ 重载的区别?"------ 答:1. 参数:后置多一个 int 占位符;2. 返回值:前置返回引用,后置返回值;3. 逻辑:前置先自增后返回,后置先返回后自增;4. 效率:前置更高(无临时对象拷贝)。

5.3 关键注意点

  1. 赋值运算符重载的必须格式
    • 参数类型:const 类名&(提高传参效率,保护原对象);
    • 返回值类型:类名&(支持连续赋值,提高返回效率);
    • 检测自赋值(this != &d);
    • 返回*this
  2. 赋值运算符只能重载为类的成员函数:不能重载成全局函数,否则与编译器生成的默认版本冲突(编译报错);
  3. 编译器生成的默认赋值运算符重载:
    • 对内置类型:浅拷贝(值拷贝);
    • 对自定义类型:调用其赋值运算符重载;
  4. 与拷贝构造的区别:拷贝构造是 "创建新对象时复制",赋值是 "已有对象间复制"。

面试小提示

常考:"哪些运算符不能重载?"------ 答:.*::sizeof?:.,共 5 个。


六、const 成员函数

6.1 通俗概念

用 const 修饰的成员函数,核心是修饰隐含的 this 指针const 类名* const this),表明该函数不能修改对象的任何成员变量,就像给对象上了 "只读锁"。

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

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

    // 非const成员函数:可以修改成员变量
    void Print() {
        cout << "Print()" << endl;
        cout << _year << "-" << _month << "-" << _day << endl;
    }

    // const成员函数:不能修改成员变量(this指针被const修饰)
    void Print() const {
        cout << "Print() const" << endl;
        cout << _year << "-" << _month << "-" << _day << endl;
        // _year = 2025; // 错误:const成员函数不能修改成员变量
    }

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

int main() {
    Date d1(2024, 5, 22); // 非const对象
    const Date d2(2024, 5, 23); // const对象

    d1.Print(); // 调用非const成员函数(输出Print())
    d2.Print(); // 调用const成员函数(输出Print() const)

    // d2.Print(); // 正确:const对象只能调用const成员函数
    // d1.Print(); // 正确:非const对象可以调用const成员函数
    return 0;
}
cpp 复制代码
Print()
2024-5-22
Print() const
2024-5-23

6.2 关键注意点

  1. const 对象只能调用 const 成员函数;
  2. 非 const 对象可以调用 const 和非 const 成员函数(优先调用非 const 版本);
  3. const 成员函数不能调用非 const 成员函数(非 const 函数可能修改成员);
  4. 非 const 成员函数可以调用 const 成员函数。

面试小提示

这是高频考点!直接考 "const 对象能否调用非 const 成员函数?"------ 答:不能,const 对象的 this 指针是const 类名*,非 const 成员函数的 this 指针是类名*,类型不兼容。


七、取地址及 const 取地址重载

7.1 通俗概念

用于获取对象的地址,编译器会自动生成默认版本,一般无需用户显式实现,只有特殊需求(如隐藏真实地址)时才需要自定义。

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

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

    // 取地址重载(默认生成的版本,无需显式写)
    Date* operator&() {
        return this; // 返回当前对象地址
    }

    // const取地址重载(默认生成的版本)
    const Date* operator&() const {
        return this; // 返回const对象地址
    }

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

int main() {
    Date d1(2024, 5, 22);
    const Date d2(2024, 5, 23);

    cout << &d1 << endl; // 调用Date* operator&(),输出d1地址
    cout << &d2 << endl; // 调用const Date* operator&() const,输出d2地址
    return 0;
}
cpp 复制代码
0x7ffee3b558a0
0x7ffee3b55890

7.2 关键注意点

  • 两个重载函数的区别:参数隐含的 this 指针,一个是Date* const,一个是const Date* const
  • 默认生成的版本已能满足需求,仅当需要 "隐藏真实地址"(如返回 nullptr 或固定地址)时,才显式实现;
  • 不能重载成全局函数,必须是类的成员函数。

八、日期类完整实现(全功能实战)

8.1 核心功能

包含构造、拷贝构造、赋值重载、日期加减、运算符重载(+、-、+=、-=、++、--、比较运算符)、日期差计算等全功能,解决进位、闰年判断等核心问题。

cpp 复制代码
#include <iostream>
#include <assert.h>
using namespace std;

class Date {
public:
    // 1. 全缺省构造函数
    Date(int year = 1900, int month = 1, int day = 1) {
        // 校验日期合法性
        if (year < 0 || month < 1 || month > 12 || day < 1 || day > GetMonthDay(year, month)) {
            cout << "非法日期!" << endl;
            assert(false);
        }
        _year = year;
        _month = month;
        _day = day;
    }

    // 2. 拷贝构造函数
    Date(const Date& d) {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

    // 3. 赋值运算符重载
    Date& operator=(const Date& d) {
        if (this != &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }

    // 4. 析构函数(无资源申请,默认即可,显式写出为了完整)
    ~Date() {}

    // 5. 获取某年某月的天数(静态成员函数,无需对象即可调用)
    static int GetMonthDay(int year, int month) {
        assert(month >= 1 && month <= 12);
        // 平年月份天数表
        static int days[13] = {0, 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 days[month];
    }

    // 6. 日期+=天数(修改当前对象,返回引用)
    Date& operator+=(int day) {
        if (day < 0) {
            return *this -= (-day); // 转成减法
        }
        _day += day;
        // 处理进位(日->月->年)
        while (_day > GetMonthDay(_year, _month)) {
            _day -= GetMonthDay(_year, _month);
            _month++;
            if (_month > 12) {
                _year++;
                _month = 1;
            }
        }
        return *this;
    }

    // 7. 日期+天数(不修改当前对象,返回新对象)
    Date operator+(int day) {
        Date temp(*this); // 拷贝当前对象
        temp += day; // 复用+=逻辑
        return temp;
    }

    // 8. 日期-=天数(修改当前对象,返回引用)
    Date& operator-=(int day) {
        if (day < 0) {
            return *this += (-day); // 转成加法
        }
        _day -= day;
        // 处理借位(日->月->年)
        while (_day < 1) {
            _month--;
            if (_month < 1) {
                _year--;
                _month = 12;
            }
            _day += GetMonthDay(_year, _month);
        }
        return *this;
    }

    // 9. 日期-天数(不修改当前对象,返回新对象)
    Date operator-(int day) {
        Date temp(*this);
        temp -= day;
        return temp;
    }

    // 10. 前置++
    Date& operator++() {
        *this += 1;
        return *this;
    }

    // 11. 后置++
    Date operator++(int) {
        Date temp(*this);
        *this += 1;
        return temp;
    }

    // 12. 前置--
    Date& operator--() {
        *this -= 1;
        return *this;
    }

    // 13. 后置--
    Date operator--(int) {
        Date temp(*this);
        *this -= 1;
        return temp;
    }

    // 14. 比较运算符重载(==)
    bool operator==(const Date& d) const {
        return _year == d._year && _month == d._month && _day == d._day;
    }

    // 15. 比较运算符重载(!=)
    bool operator!=(const Date& d) const {
        return !(*this == d);
    }

    // 16. 比较运算符重载(<)
    bool operator<(const Date& d) const {
        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;
    }

    // 17. 比较运算符重载(<=)
    bool operator<=(const Date& d) const {
        return *this < d || *this == d;
    }

    // 18. 比较运算符重载(>)
    bool operator>(const Date& d) const {
        return !(*this <= d);
    }

    // 19. 比较运算符重载(>=)
    bool operator>=(const Date& d) const {
        return !(*this < d);
    }

    // 20. 日期-日期(返回天数差,d1 - d2 = 相差天数)
    int operator-(const Date& d) const {
        Date max = *this;
        Date min = d;
        int flag = 1;
        if (*this < d) {
            max = d;
            min = *this;
            flag = -1;
        }
        int dayCount = 0;
        while (min < max) {
            min++;
            dayCount++;
        }
        return dayCount * flag;
    }

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

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

// 测试代码
int main() {
    Date d1(2024, 5, 22);
    Date d2(2024, 6, 1);

    // 测试日期+
    Date d3 = d1 + 10;
    cout << "d1 + 10 = ";
    d3.Print(); // 输出:2024-6-1

    // 测试日期-
    Date d4 = d2 - 10;
    cout << "d2 - 10 = ";
    d4.Print(); // 输出:2024-5-22

    // 测试++
    Date d5 = ++d1;
    cout << "++d1 = ";
    d5.Print(); // 输出:2024-5-23
    Date d6 = d1++;
    cout << "d1++ = ";
    d6.Print(); // 输出:2024-5-23
    cout << "d1 = ";
    d1.Print(); // 输出:2024-5-24

    // 测试比较运算符
    cout << "d1 < d2: " << (d1 < d2) << endl; // 输出:1(true)

    // 测试日期差
    int diff = d2 - d1;
    cout << "d2 - d1 = " << diff << "天" << endl; // 输出:9天

    return 0;
}
cpp 复制代码
d1 + 10 = 2024-6-1
d2 - 10 = 2024-5-22
++d1 = 2024-5-23
d1++ = 2024-5-23
d1 = 2024-5-24
d1 < d2: 1
d2 - d1 = 9天

8.2 关键注意点

  • 日期合法性校验:构造函数中校验年、月、日是否合法,避免非法日期;
  • 闰年判断:GetMonthDay中通过(year%4==0&&year%100!=0)||(year%400==0)判断;
  • 进位 / 借位处理:+=/-=中循环处理日、月、年的进位 / 借位;
  • 代码复用:+复用+=-复用-=,减少冗余;
  • const 成员函数:比较运算符、Print 等不修改对象的函数,均设为 const 成员函数,支持 const 对象调用。

九、学习总结与建议

  • 核心逻辑:本章围绕 "默认成员函数" 和 "运算符重载" 展开,日期类是实战核心,需掌握 "资源管理" 和 "语法规则" 两大关键点;
  • 重点突破:
    • 构造 / 析构 / 拷贝构造 / 赋值重载:何时显式实现(资源申请)、浅拷贝 vs 深拷贝;
    • 运算符重载:前置 ++/ 后置 ++ 的区别、日期加减的进位 / 借位、比较运算符的逻辑;
    • 日期类实现:闰年判断、月份天数计算、日期差计算的核心算法;
  • 学习方法:
    • 亲手运行日期类代码,修改参数测试边界情况(如 2 月 29 日、12 月 31 日);
    • 对比记忆:拷贝构造 vs 赋值重载、前置 ++vs 后置 ++、+vs+=;
    • 聚焦面试:日期类实现(高频笔试题)、const 成员函数调用规则、运算符重载禁止情况;
  • 避坑清单:
    • 日期类必须校验合法性,否则会出现逻辑错误;
    • 运算符重载不能改变内置类型含义,不能创建新运算符;
    • 涉及资源申请的类,必须显式实现深拷贝,否则崩溃;
    • 后置 ++ 必须返回值,前置 ++ 返回引用。

相关推荐
小程同学>o<1 小时前
嵌入式之C/C++(三)指针
c语言·c++·算法·嵌入式软件·嵌入式面试题库
lxl13071 小时前
学习C++(4)构造函数+析构函数+拷贝构造函数
开发语言·c++·学习
阿kun要赚马内2 小时前
Qt写群聊项目(二):客户端
开发语言·c++·qt
轩情吖2 小时前
数据结构-并查集
开发语言·数据结构·c++·后端··并查集
YYYing.2 小时前
【Linux/C++进阶篇 (一)】man手册、gdb调试、静态库与动态库
linux·运维·c++
孞㐑¥2 小时前
算法—模拟
c++·经验分享·笔记·算法
2401_891450462 小时前
C++中的职责链模式实战
开发语言·c++·算法
m0_708830962 小时前
C++中的原型模式变体
开发语言·c++·算法
Trouvaille ~2 小时前
【Linux】Linux线程概念与控制(四):glibc源码剖析与实现原理
linux·运维·服务器·c++·操作系统·glibc·线程控制