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

类与对象(中)笔记整理

一、类的 6 个默认成员函数概述

首先明确类的 6 个默认成员函数(编译器在用户未显式定义时会自动生成):

  1. 构造函数:初始化对象
  2. 析构函数:清理对象资源
  3. 拷贝构造函数:用已有对象初始化新对象
  4. 赋值运算符重载:已有对象间的赋值
  5. 取地址运算符重载:获取对象地址
  6. const 取地址运算符重载:获取 const 对象地址

默认成员函数的核心特点:仅当用户未显式定义对应函数时,编译器才会生成;若用户显式定义,编译器则不再生成。

二、构造函数

2.1 概念

构造函数是类的特殊成员函数,核心作用是初始化对象的成员变量(而非开辟对象存储空间),在对象创建时由编译器自动调用。

2.2 特性

  1. 函数名与类名完全一致(如 Date 类的构造函数名必为 Date)。
  2. 无返回值(无需声明 void 或其他类型,直接定义函数体)。
  3. 支持函数重载(可定义多个参数列表不同的构造函数)。
  4. 若用户未显式定义,编译器生成无参默认构造函数(仅初始化自定义类型成员,内置类型成员不初始化)。

2.3 构造函数的常见实现形式(以 Date 类为例)

(1)无参构造函数
// 无参构造:创建对象时无需传参,仅初始化成员变量(此处未赋值,内置类型成员为随机值)
class Date {
public:Date() {// 若未给_year/_month/_day赋值,其值为随机(内置类型特性)// 可手动初始化:_year = 0; _month = 1; _day = 1;}
private:int _year;   // 内置类型int _month;int _day;
};// 调用方式:Date d; (无需传参,触发无参构造)

讲解:无参构造属于 “默认构造函数”(无需参数即可调用),但仅定义无参构造时,若需指定日期初始化(如 2025,10,11),需额外定义带参构造。

(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, 10, 11); (必须传3个int参数,触发带参构造)

讲解:带参构造解决了无参构造无法自定义初始化的问题,但需注意:若仅定义带参构造,未定义无参构造,则无法调用Date d;(编译器不再生成默认无参构造)。

(3)全缺省构造函数(推荐)
// 全缺省构造:所有参数均设置默认值,兼容“无参”到“全参”所有初始化场景
class Date {
public:// 参数默认值:year默认0,month默认1,day默认1(默认日期可自定义)Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};// 调用方式(灵活适配):
Date d1;                  // 无参:使用所有默认值 → 0年1月1日
Date d2(2025);            // 传1参:year=2025,month=1,day=1
Date d3(2025, 10);        // 传2参:year=2025,month=10,day=1
Date d4(2025, 10, 11);    // 传3参:year=2025,month=10,day=11

讲解:全缺省构造是 “默认构造函数” 的最优形式 —— 仅需一个函数,覆盖所有初始化场景,避免多重重载的冗余代码。补充注意:默认构造函数只能有一个(若同时定义无参构造和全缺省构造,编译器会报错 “默认构造函数不明确”)。

2.4 编译器生成的默认构造函数特性

若用户未显式定义任何构造函数,编译器生成的无参默认构造函数,对成员变量的处理规则如下:

成员变量类型处理方式原因
内置类型(int、double 等)不主动初始化(值为随机)1. 兼容 C 语言行为(C 中局部变量默认不初始化);2. 性能优化(避免不必要的初始化开销)
自定义类型(如 Time 类、Stack 类)调用该自定义类型的默认构造函数保证自定义类型成员的合法性(避免其处于无效状态,如未初始化的指针)

示例验证

// 自定义类型Time(有自己的默认构造)
class Time {
public:Time() {_hour = 0;   // 显式初始化内置类型成员_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};// Date类(用户未定义构造函数,编译器生成默认构造)
class Date {
private:int _year;   // 内置类型:默认构造不初始化 → 随机值Time _t;     // 自定义类型:默认构造调用Time的默认构造 → _t._hour=0
};// 测试:
Date d;
// d._year:随机值;d._t._hour:0(因Time的默认构造初始化)

三、析构函数

3.1 概念

析构函数是类的特殊成员函数,核心作用是清理对象生命周期内占用的资源(如动态内存、文件句柄等),在对象生命周期结束时(如局部对象出作用域、动态对象被 delete)由编译器自动调用。

3.2 特性

  1. 函数名:类名前加~(如 Date 类的析构函数名为~Date)。
  2. 无参数、无返回值(无法重载,一个类仅一个析构函数)。
  3. 若用户未显式定义,编译器生成默认析构函数(仅清理自定义类型成员)。

3.3 析构函数的实现(以 Stack 类为例)

Stack 类需动态开辟内存(_a指向堆空间),必须显式定义析构函数释放资源,否则会导致内存泄漏:

class Stack {
public:// 构造函数:动态开辟内存Stack(int capacity = 4) {_a = (int*)malloc(sizeof(int) * capacity);  // 堆内存分配if (_a == nullptr) {// 处理内存分配失败(补充用户可能疏漏的异常场景)perror("malloc fail");exit(-1);}_size = 0;_capacity = capacity;}// 析构函数:释放动态开辟的内存(核心清理逻辑)~Stack() {if (_a != nullptr) {  // 避免野指针释放free(_a);         // 释放堆内存_a = nullptr;     // 置空指针,防止后续误操作_size = 0;_capacity = 0;}}
private:int* _a;     // 指向堆内存的指针(需手动释放)int _size;   // 内置类型(无需特殊清理)int _capacity;
};// 调用场景:
void test() {Stack st(10);  // 创建对象:调用构造函数,开辟10个int的堆空间
}  // st出作用域:调用析构函数,释放_a指向的堆内存(无内存泄漏)

3.4 编译器生成的默认析构函数特性

与默认构造函数类似,编译器生成的默认析构函数,对成员变量的处理规则:

  • 内置类型成员:不做任何清理(因内置类型无 “资源” 可言,如 int 无需释放)。
  • 自定义类型成员:调用该自定义类型的析构函数(清理其占用的资源)。

补充注意:若类中包含动态资源(如指针_a),必须显式定义析构函数 —— 否则编译器默认析构函数仅释放自定义类型成员,动态资源会泄漏。

四、拷贝构造函数

4.1 概念

拷贝构造函数是类的特殊成员函数,核心作用是用已存在的对象(实参)初始化新创建的对象,本质是 “对象的复制初始化”(如Date d2(d1);Date d3 = d1;)。

4.2 特性

  1. 函数名与类名一致(同构造函数)。
  2. 参数列表:必须是 “const 类类型 &”(const 修饰防止修改实参,引用传递避免无限递归)。
  3. 若用户未显式定义,编译器生成默认拷贝构造函数(浅拷贝:按字节复制成员变量)。

4.3 拷贝构造函数的参数传递陷阱(值传递 vs 引用传递)

(1)错误形式:值传递参数(触发无限递归)
// 错误示例:拷贝构造函数参数为值传递(Date d)
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 错误:参数为值传递Date(Date d) {  // 调用时需将实参d1复制给形参d → 触发拷贝构造_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};// 调用:Date d2(d1); → 触发无限递归

递归原因分析

  1. 执行Date d2(d1);时,需调用拷贝构造函数,实参为 d1,形参为 d(值传递)。
  2. 值传递的本质是 “用实参初始化形参”,即Date d = d1; → 再次触发拷贝构造函数。
  3. 新的拷贝构造调用又需值传递参数,再次触发拷贝构造…… 无限循环,编译报错。
(2)正确形式:const 引用传递参数
// 正确示例:参数为const Date&(引用传递,无拷贝)
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 正确:const引用传递Date(const Date& d) {  // d是d1的别名,无需复制,不触发新的拷贝构造_year = d._year;   // 用d(即d1)的成员初始化新对象d2_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};// 调用:Date d2(d1); → 正常执行,无递归

const 修饰的必要性

  • 防止在拷贝构造函数内部误修改实参 d(如d._year = 2026;)。
  • 支持用 const 对象初始化新对象(如const Date d1(2025,10,11); Date d2(d1);,非 const 引用无法接收 const 实参)。

4.4 常见错误的拷贝构造实现(用户示例分析)

用户提供的错误代码:

// 错误的拷贝构造函数
class Date {
public:Date(const Date& d) {d._day = _day;  // 两处错误// ... 其他成员赋值}
private:int _year;int _month;int _day;
};

错误分析

  1. 违反 const 约束:dconst Date&(常引用),代表其指向的对象不可修改,而d._day = _day;试图修改 d 的成员 → 编译报错。
  2. 赋值逻辑颠倒:拷贝构造的目的是 “用实参 d 的成员初始化新对象(this 指向的对象)”,正确逻辑应为_day = d._day;(新对象成员 = 实参成员),而非反向赋值。

4.5 编译器生成的默认拷贝构造函数(浅拷贝)

若用户未显式定义拷贝构造函数,编译器生成的默认拷贝构造函数采用浅拷贝(按字节复制) —— 将实参对象的每个成员变量值,直接复制到新对象的对应成员。

(1)浅拷贝的适用场景

当类的成员变量均为内置类型(无动态资源)时,浅拷贝可正常工作,无需显式定义拷贝构造。例如 Date 类:

class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 未显式定义拷贝构造,编译器生成默认浅拷贝
private:int _year;  // 内置类型,浅拷贝无问题int _month;int _day;
};// 测试:
Date d1(2025,10,11);
Date d2(d1);  // 浅拷贝:d2._year = d1._year,d2._month = d1._month,d2._day = d1._day → 正常
(2)浅拷贝的致命问题(含动态资源的类)

当类包含动态资源(如指针、堆内存)时,浅拷贝会导致 “同一块资源被重复释放”,程序崩溃。以 Stack 类为例:

// Stack类(未显式定义拷贝构造,用编译器默认浅拷贝)
class Stack {
public:Stack(int capacity = 4) {_a = (int*)malloc(sizeof(int)*capacity);_size = 0;_capacity = capacity;}~Stack() {  // 显式定义析构函数,释放堆内存if (_a) {free(_a);_a = nullptr;}}
private:int* _a;     // 动态资源(堆内存指针)int _size;int _capacity;
};// 测试:浅拷贝导致崩溃
void test() {Stack st1(10);  // st1._a指向堆内存(地址0x1234)Stack st2(st1); // 浅拷贝:st2._a = st1._a → 0x1234(同一块内存)// 生命周期结束,调用析构函数:// 1. st2先析构:free(st2._a) → 释放0x1234// 2. st1再析构:free(st1._a) → 再次释放0x1234(非法操作,程序崩溃)
}

解决方案:显式定义 “深拷贝” 的拷贝构造函数 —— 为新对象分配独立的动态资源,再复制实参的资源内容,而非直接复制指针地址:

// Stack类的深拷贝构造(补充用户疏漏的深拷贝实现)
class Stack {
public:Stack(int capacity = 4) {_a = (int*)malloc(sizeof(int)*capacity);if (!_a) { perror("malloc fail"); exit(-1); }_size = 0;_capacity = capacity;}// 深拷贝构造Stack(const Stack& st) {// 1. 为新对象分配独立的堆内存_a = (int*)malloc(sizeof(int)*st._capacity);if (!_a) { perror("malloc fail"); exit(-1); }// 2. 复制实参的资源内容(而非指针地址)memcpy(_a, st._a, sizeof(int)*st._size);  // 内存拷贝函数_size = st._size;_capacity = st._capacity;}~Stack() {if (_a) { free(_a); _a = nullptr; }}
private:int* _a;int _size;int _capacity;
};// 测试:深拷贝无崩溃
Stack st1(10);
Stack st2(st1);  // st2._a指向新堆内存(如0x5678),与st1._a(0x1234)独立
// 析构时分别释放各自的内存,无重复释放问题

4.6 拷贝构造函数的调用场景

  1. 用已存在对象初始化新对象(如Date d2(d1);Date d3 = d1;)。
  2. 函数参数为类类型(值传递):
    void PrintDate(Date d) {  // 值传递:调用Date的拷贝构造,用实参初始化形参d// ...
    }
    Date d1(2025,10,11);
    PrintDate(d1);  // 触发拷贝构造
    
  3. 函数返回值为类类型(值传递,C++11 后可能被优化,但需了解原始逻辑):
    Date GetDate() {Date d(2025,10,11);return d;  // 值传递返回:调用拷贝构造,用d初始化临时对象
    }
    Date d2 = GetDate();  // 触发拷贝构造(部分编译器优化后可能省略)
    

五、赋值运算符重载

5.1 运算符重载基础

5.1.1 概念

运算符重载是 C++ 为自定义类型提供的特性,核心目的是让自定义类型(如 Date、Stack)能像内置类型(int、double)一样使用标准运算符(如+===),提升代码可读性。

本质:将运算符转换为对应的函数调用(如d1 == d2等价于operator==(d1, d2))。

5.1.2 运算符重载的规则
  1. 函数格式:返回值类型 operator运算符(参数列表) { 运算符逻辑 }例:重载==判断日期相等,函数名为operator==

  2. 核心限制:

    • 不能创造新运算符(如operator@operator#,需为 C++ 原生运算符)。
    • 至少一个操作数为 “类类型” 或 “枚举类型”(禁止重载纯内置类型的运算符,如int operator+(int a, int b)—— 编译器报错)。
    • 不破坏内置类型的运算符语义(如重载int operator+(int a, int b)时,逻辑不能是 “a - b”,需符合运算符原意)。
    • 成员函数重载时隐含this指针:成员函数的参数个数 = 运算符操作数个数 - 1(this指向左操作数)。
  3. 5 个无法重载的运算符(笔试高频考点):

    运算符名称无法重载的原因
    .类成员访问符语法基础,重载会破坏对象成员访问逻辑
    .*类成员指针访问符.类似,涉及成员指针的核心语法
    ::作用域解析符用于区分全局 / 类 / 命名空间的成员,无重载必要
    sizeof计算大小运算符操作数是类型或对象,结果为编译期常量,无需重载
    ?:三目条件运算符运算符优先级和结合性复杂,重载会导致歧义

5.2 赋值运算符重载(operator=)

5.2.1 概念

赋值运算符重载是特殊的运算符重载,核心作用是将已存在的对象(右操作数)的值,赋值给另一个已存在的对象(左操作数)(区别于拷贝构造:拷贝构造是 “用已有对象初始化新对象”)。

5.2.2 赋值运算符重载的实现(以 Date 类为例)
(1)成员函数实现(推荐,编译器默认生成的也是成员函数)
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 赋值运算符重载(成员函数)Date& operator=(const Date& d) {  // 返回值为引用,参数为const引用// 1. 自赋值检查(避免自己赋值给自己,如d1 = d1)if (this != &d) {  // this是左操作数指针,&d是右操作数地址_year = d._year;   // 赋值成员变量_month = d._month;_day = d._day;}return *this;  // 返回左操作数(支持连续赋值)}
private:int _year;int _month;int _day;
};
(2)关键细节解析
  1. 参数为何是const Date&

    • const:防止修改右操作数(如d1 = d2时,不允许在重载函数中修改d2)。
    • 引用:避免传值导致的拷贝构造(减少性能开销,尤其是对象较大时)。
  2. 返回值为何是Date&(引用),而非Date(值传递)?

    返回类型特点问题
    Date&(引用)返回左操作数的别名(*this),无拷贝构造无问题,支持连续赋值
    Date(值传递)返回时需复制*this生成临时对象,触发拷贝构造1. 性能开销;2. 无法支持连续赋值(临时对象是右值,无法作为左操作数)

    连续赋值支持示例d1 = d2 = d3; → 等价于d1.operator=(d2.operator=(d3));

    • 先执行d2.operator=(d3),返回d2(引用);
    • 再执行d1.operator=(d2),完成赋值。
  3. 为何需要 “自赋值检查”(if (this != &d))?

    • 若没有检查,当执行d1 = d1时,会重复执行赋值逻辑(虽对 Date 类无影响,但对含动态资源的类会致命)。例如 Stack 类的赋值重载:
      Stack& Stack::operator=(const Stack& st) {// 无自赋值检查:d1 = d1时,先free(_a),再malloc → 原资源已释放,复制内容出错if (this != &st) {  // 必须检查free(_a);  // 释放左操作数原有资源_a = (int*)malloc(sizeof(int)*st._capacity);memcpy(_a, st._a, sizeof(int)*st._size);_size = st._size;_capacity = st._capacity;}return *this;
      }
      
5.2.3 编译器生成的默认赋值运算符重载

若用户未显式定义赋值运算符重载,编译器生成的默认版本采用浅拷贝(与默认拷贝构造一致):

  • 适用场景:类无动态资源(如 Date 类),浅拷贝可正常赋值。
  • 问题场景:类含动态资源(如 Stack 类),浅拷贝导致 “重复释放资源” 或 “内存泄漏”(同拷贝构造的浅拷贝问题),需显式定义深拷贝的赋值运算符重载。

5.3 其他运算符重载示例(以 == 为例)

(1)全局函数实现(非成员函数)
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 需将成员变量设为public,或提供getter函数(否则全局函数无法访问)int _year;int _month;int _day;
};// 全局函数重载==:判断两个Date对象是否相等
bool operator==(const Date& d1, const Date& d2) {return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}// 调用方式:
Date d1(2025,10,11), d2(2025,10,12);
cout << (d1 == d2) << endl;  // 等价于operator==(d1, d2) → 输出0(false)
(2)成员函数实现
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 成员函数重载==:隐含this指针(左操作数)bool operator==(const Date& d) {  // 参数d是右操作数// this指向左操作数(如d1 == d2时,this = &d1)return this->_year == d._year && _month == d._month  // 可省略this->,编译器自动补充&& _day == d._day;}
private:int _year;int _month;int _day;
};// 调用方式:
Date d1(2025,10,11), d2(2025,10,11);
cout << (d1 == d2) << endl;  // 等价于d1.operator==(d2) → 输出1(true)

六、const 成员

6.1 const 修饰类的成员函数(const 成员函数)

6.1.1 概念

const 成员函数是指用const修饰的类成员函数,核心作用是限制该函数不能修改类的成员变量,保证函数的 “只读” 属性。

6.1.2 语法与原理
  1. 语法格式:返回值类型 函数名(参数列表) const { 函数体 }例:void Print() const;(const 写在参数列表后、函数体前)。

  2. 原理:类的成员函数隐含this指针(类型为类类型* const,如 Date 类成员函数的thisDate* const)。当函数被const修饰时,this指针的类型变为const 类类型* const(如const Date* const)—— 第一个const限制 “不能通过this修改对象成员”,第二个const限制 “this指针本身不能修改指向”。

6.1.3 const 成员函数的调用规则
对象类型可调用的成员函数类型原因
普通对象(非 const)const 成员函数、非 const 成员函数普通对象允许被修改,也允许只读访问
const 对象仅 const 成员函数const 对象禁止被修改,非 const 成员函数可能修改成员,故禁止调用

示例验证(用户错误代码修正)

// 错误示例:const对象调用非const成员函数
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 非const成员函数(可能修改成员变量)void Print() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};void f(const Date& d) {  // d是const引用(const对象)d.Print();  // 编译报错:const对象不能调用非const成员函数
}
// 正确示例:将Print改为const成员函数
class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 正确:const成员函数(只读,不修改成员)void Print() const {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};void f(const Date& d) {d.Print();  // 编译通过:const对象可调用const成员函数
}
6.1.4 const 成员函数的内部调用规则

const 成员函数内部仅能调用其他 const 成员函数,不能调用非 const 成员函数 —— 因非 const 成员函数可能修改成员变量,违反 const 成员函数的 “只读” 约定。

class Date {
public:void f1() const {  // const成员函数f2();  // 正确:f2是const成员函数// f3();  // 错误:f3是非const成员函数}void f2() const {  // const成员函数// ...}void f3() {  // 非const成员函数// ...}
private:int _year;int _month;int _day;
};

6.2 const 修饰类的成员变量

除了修饰成员函数,const 还可修饰类的成员变量(const 成员变量),特性如下:

  1. 必须在构造函数的初始化列表中初始化(不能在构造函数体中赋值,因 const 变量初始化后不能修改)。
  2. 一旦初始化,终身不可修改。

示例

class Circle {
public:// 初始化列表:初始化const成员变量_radiusCircle(double radius) : _radius(radius) {// _radius = radius;  // 错误:const变量不能赋值,只能初始化}
private:const double _radius;  // const成员变量(圆的半径,初始化后不可改)double _area;
};

七、取地址及 const 取地址运算符重载

7.1 概念

取地址运算符重载(operator&)和 const 取地址运算符重载(operator&() const)是类的默认成员函数,核心作用是返回对象的地址,默认行为是返回this指针(普通对象返回类类型*,const 对象返回const 类类型*)。

7.2 特性

  1. 若用户未显式定义,编译器自动生成默认版本(返回this)。
  2. 通常无需显式定义(默认行为满足需求),仅在特殊场景下重写(如隐藏对象真实地址,返回假地址)。

7.3 实现示例

class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// 1. 普通取地址运算符重载(返回普通对象地址)Date* operator&() {return this;  // 默认行为,可省略不写}// 2. const取地址运算符重载(返回const对象地址)const Date* operator&() const {return this;  // 默认行为,可省略不写}
private:int _year;int _month;int _day;
};// 调用:
Date d1(2025,10,11);
const Date d2(2025,10,12);Date* p1 = &d1;          // 调用Date* operator&() → 返回&d1
const Date* p2 = &d2;    // 调用const Date* operator&() const → 返回&d2

八、日期类(Date)完整实现整合

基于上述知识点,整合 Date 类的核心功能(构造、拷贝构造、赋值重载、== 重载、Print 函数),形成完整示例:

#include <iostream>
using namespace std;class Date {
public:// 1. 全缺省构造函数(默认构造)Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;// 补充:可添加日期合法性检查(用户疏漏的健壮性逻辑)if (!IsValidDate()) {cout << "日期非法!" << endl;// 可选择初始化默认日期或终止程序_year = 0;_month = 1;_day = 1;}}// 2. 拷贝构造函数(深拷贝,Date类无动态资源,浅拷贝即可)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类无动态资源,可省略,编译器生成默认版本)~Date() {}// 5. ==运算符重载(判断日期相等)bool operator==(const Date& d) const {return _year == d._year&& _month == d._month&& _day == d._day;}// 6. const成员函数(打印日期,只读)void Print() const {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;// 补充:日期合法性检查(私有成员函数,仅内部调用)bool IsValidDate() const {// 月份范围:1-12if (_month < 1 || _month > 12) return false;// 每月天数(简化版,未处理闰年2月)int daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 天数范围:1-当月最大天数if (_day < 1 || _day > daysInMonth[_month]) return false;return true;}
};// 测试函数
int main() {Date d1(2025, 10, 11);  // 全缺省构造(传3参)Date d2(d1);             // 拷贝构造Date d3 = d2;            // 拷贝构造(等价于Date d3(d2))Date d4;                 // 全缺省构造(无参,默认0-1-1)d4 = d1;                 // 赋值运算符重载d1.Print();  // 输出2025-10-11d2.Print();  // 输出2025-10-11d3.Print();  // 输出2025-10-11d4.Print();  // 输出2025-10-11cout << (d1 == d2) << endl;  // 输出1(true)cout << (d1 == d4) << endl;  // 输出1(true)Date d5(2025, 13, 1);  // 非法日期,打印“日期非法!”,初始化0-1-1d5.Print();            // 输出0-1-1return 0;
}

补充说明

  • 新增IsValidDate函数实现日期合法性检查(用户疏漏的健壮性逻辑),避免创建非法日期(如 2025 年 13 月 1 日)。
  • 析构函数因 Date 类无动态资源,可省略,编译器生成的默认版本足够使用。
http://www.dtcms.com/a/479091.html

相关文章:

  • 16.shell编程-函数
  • 怎样让自己网站的文章被百度收录自己在家做网站
  • 青岛行业网站建设电话佛山网站建设方案服务
  • 书店网站建设设计方案小程序可以做企业网站
  • C++面试(1)
  • 2025年主流运动耳机测评,园世Beta pro怎么样?
  • icp备案网站信息wordpress add_action 是什么意思
  • 7-数组的概念和使用
  • 基数排序算法实现
  • 10.9 DevEco Studio安装
  • 健康风险评估实训室:功能与实训场景介绍
  • 哈尔滨专业网站营销通化县住房和城乡建设局网站
  • vue2使用wangEditor:上传图片,视频,设置表格,自定义初始化字体和大小
  • 调试BMI088(X5平台)
  • 微信群领券网站怎么做wordpress七牛同步上传
  • EtherNet/IP转ModbusTCP协议转换器实现水质数据零延迟交互
  • 拌合站软件开发(27)监测各项IP设备可访问性
  • 网站设计论文答辩问题及答案万能回答企业管理培训课程推荐
  • Cannot Run Git: error launching git:
  • 模板建站源码wordpress调用二级分类目录
  • 车载刷写框架 --- 刷写过程中擦除相关思考
  • 初中做语文题的网站怎么开网站 第一步怎么做
  • 漯河网站推广哪家好微信小程序模板使用
  • 【大模型实战笔记 3】大模型Function Call的原理及应用
  • 校园网站的作用广州网络推广外包平台
  • 微网站 获取手机号凡客诚品网站建设策划书
  • MyCat 实战:订单与用户数据的水平分库分表 + MySQL 读写分离完整方案
  • Learning Path Recommendation
  • 【足式机器人控制】名义落足点与Raibert落足点详细讲解
  • 简单聊聊数据可视化大屏制作的前端设计与后端开发