资讯动态

C++(类和对象中)

发布时间:2026/10/6 12:38:02 来源:尧图企业网站定制
一、前言本节用Date类练了6个默认成员函数运算符重载二、六个默认成员函数构造函数对象创建时初始化成员析构函数对象销毁时清理资源拷贝构造函数用已有对象创建新对象赋值运算符重载两个已存在对象之间赋值取地址运算符重载返回对象地址const取地址运算符重载返回const对象地址2.1 核心规则六个函数不写的时候编译器会自动生成默认的版本。写了某一个函数的时候那个函数就不再生成默认的版本。示例声明析构不影响拷贝构造、赋值重载、取地址的自动生成class A { public: ~A() { } // 只声明/实现了析构 }; A a1; A a2(a1); // 照样能编译拷贝构造是编译器自动生成的特殊情况只要你自己写了任意一个构造函数带参构造、拷贝构造都算编译器就不再送默认无参构造原因隐式默认无参构造的生成条件是一个构造函数都没写过。class B { public: B(int x) { } // 写了带参构造 }; B b; // ❌ 编译错误默认无参构造不再生成 B b2(10); // ✓三、拷贝构造3.1 拷贝构造是什么拷贝构造函数用已有对象创建新对象时调用的特殊构造函数。三种触发场景Date d1(2024, 5, 20); Date d2(d1); // ① 直接调用 Date d3 d1; // ② 也是拷贝构造 void f(Date d) {} // ③ 传值传参f(d1) 时拷贝一份其中 Date d3d1实际是拷贝构造真正的赋值是d3d1(两个已存在的对象)3.2 为什么参数必须是const Date两个设计点:必须用引用写成值传递Date(Date d)的话创建形参d时又要拷贝实参-又调用拷贝构造-无限递归。 所以 C 规定拷贝构造必须传引用加 const拷贝时不该修改被拷贝的对象而且只有 const 引用才能拷贝 const 对象和临时对象3.3 默认拷贝构造是浅拷贝不自己写时编译器会自动生成——把成员逐个拷贝浅拷贝。对只有 int 成员的 Date 来说够用但以后成员里有指针如 String 类时浅拷贝会让两个对象指向同一块内存析构时 double free所以那时必须自己写深拷贝。3.4 我的实现Date::Date(const Date d) : _year(d._year), _month(d._month), _day(d._day) { }两点说明- 用初始化列表参数→成员直接初始化- d._year 是 private但 C 的 private 是类级别同一个类的对象之间可以互访私有成员所以完全合法3.5 测试验证Date d1(2024, 5, 20); Date d2(d1); Date d3 d1; d2.Print(); // 2024-5-20 d3.Print(); // 2024-5-20四、赋值重载4.1 赋值重载是什么赋值运算符重载给自定义类型定义的行为让两个已经存在的对象之间可以互相赋值。 本来只服务于内置类型int、double……C 允许我们给自定义类型Date重新定义 的含义——这就是赋值运算符重载函数名固定叫 operatorDate Date::operator(const Date d) // ↑ ↑ ↑ ↑ //返回类型 类域 函数名 参数右操作数如我们写d5d4时编译器翻译成:d5.operator(d4); // 左操作数 d5 → this右操作数 d4 → 参数 d默认版本不自己写时编译器自动生成逐成员浅拷贝版——对 Date 够用以后有指针成员的类String必须自己写深拷贝。4.2 什么时候被调用调用条件两个已经存在的对象之间使用 。代码示例Date d5(2024, 12, 31); // d5 已存在 Date d4(2000, 1, 1); // d4 已存在 d5 d4; // ← 此刻被调用翻译成 d5.operator(d4) Date a, b, c; // 三个老对象 a b c; // 链式赋值每步 都调用一次 d5 Date(2024, 1, 1); // 右边是临时对象也调用4.3 和拷贝构造的区别代码示例Date d4(2000, 1, 1); Date d5(2024, 12, 31); Date d6 d4; // ← 这行是拷贝构造 d5 d4; // ← 这行才是赋值重载4.4 为什么返回Date三个设计问题1. 为什么不是 void 赋值要支持链式 a b c;——b c 的结果还要再赋给 a2. 为什么是引用而不是值 避免多一次拷贝3. return *this; 是什么 返回左操作数本身4.5 自赋值检查 this ! d防止自己给自己赋值。日期类里不检查也不会崩成员全是 int但它是给以后有资源的类如 String准备的那种类要先释放自己的指针、再从 d 拷贝如果 d 就是自己就会读到已释放的内存直接崩溃。所以现在养成习惯。4.6 我的实现 测试验证其中d5d4相当于d5.operator(d4);五、运算符重载5.1 基本规则与翻译表C 允许我们给自定义类型重新定义运算符让 Date 对象也能写出 d 100、d1 d2 这样自然的代码。核心认知运算符重载函数的名字是 operator符号。比如 operator 就是加号的学名operator 是 的学名。写表达式时编译器会自动翻译成函数调用我写的 编译器翻译成d 100 d.operator(100)d 100 d.operator(100)d / d d.operator() / d.operator(0)d1 d2 d1.operator(d2)d1 d2 d1.operator(d2)三条规则要点左操作数 → this右操作数 → 参数这就是 只能是成员函数的原因不能重载的运算符只有 5 个. .* :: ?: sizeof口诀点、点星、双冒号、问号冒号、sizeof、[]、()、- 必须重载为成员函数其余运算符成员/全局皆可5.2 与 一个改自己一个造新对象两者最大的区别d 100 d 100对 d 的影响 修改 d 自己 不改 d产生新日期返回类型 Date返回自己 Date新对象 的实现——把天数加到日上超界就逐月进位例2024-1-1 100 → 101 → 101-31702月→ 70-29413月→ 41-31104月→ 2024-4-10 的实现——复用 拷贝自己 → 对拷贝加 → 返回拷贝为什么返回 Date 值而不是引用因为 tmp 是函数内的局部对象返回它的引用会悬空。- 和 - 是镜像逻辑借位日数 ≤ 0 时向前一个月借天数月份退到 0 就跨年。5.3 前置/后置 与 --签名区别高频考点后置版本多一个 int 哑元参数纯粹为了和前置区分函数体里根本不用它表达式 翻译d d.operator()d d.operator(0)行为区别前置先加返回加完后的自己后置先存旧值再加返回旧值。心得后置版先拷一份旧值所以比前置多一次拷贝——自定义类型优先用前置 d。5.4 比较运算符只写两个其余组装测试结果代码5.5 日期减日期- 的第二个版本写到这里日期类的 - 其实有两副面孔表达式 调用的函数 返回d1 - 100 Date operator-(int day) 新日期d1 - d2 int operator-(const Date d) 两个日期相差的天数编译器根据参数类型自动选版本参数是 int 选第一个参数是 Date 选第二个——这就是重载解析。代码示例

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑