C++面向对象实战:多项式类与String类的内存安全实现
简介:本资源是吉林大学《面向对象程序设计》课程配套的C++上机实验报告,面向计算机专业本科生及C++初学者,聚焦面向对象核心能力训练,解决多项式类设计中动态内存管理、运算符重载、多文件组织等典型实践难点。压缩包为单个303KB的Word文档(.doc),完整呈现了从需求分析、类设计(Polynomial.h/.cpp)、关键代码实现(含构造/析构/拷贝构造、input/output/calculate函数)、运算符重载细节(+、-、*、=,含友元考量)到主函数测试的全流程,附有静态成员计数、错误输入处理、系数为0的输出优化等工程化细节。已有1810人学习下载,读者可直接复用该报告结构撰写课程作业,深入理解类封装、资源管理与const成员函数设计原则,并获得可编译运行的完整多文件代码框架与规范注释。
1. 这不是一份交差作业,而是一套能跑通、能调试、能改出新功能的 C++ 面向对象实战模板
你有没有遇到过这种场景:刚学完 C++ 类、构造函数、运算符重载,一写多项式就卡在new和delete[]的配对上?输入一个系数输错,程序直接崩;两个多项式相乘结果全是乱码;operator=写完一运行就报double free or corruption?这不是你手生——是原始实验报告里藏着一堆没填坑的内存黑匣子、没声明 const 的接口、没处理边界 case 的输出逻辑,更别说多文件编译时头文件循环包含、静态成员未初始化、友元函数缺失这些“玄学翻车点”。
这份来自吉林大学《面向对象程序课程设计》的上机实验报告,表面看是五份独立 C++ 实验(多项式类、String 类、继承体系、工资系统、图形管理),但实际是一套完整覆盖 C++ 核心机制的工程化训练链:从动态内存管理(A 部分)、深拷贝/浅拷贝陷阱(E 部分),到访问控制与继承语义(C 部分)、抽象基类与多态调度(D 部分),再到资源封装与 RAII 初探(B 部分)。它不教你怎么背语法,而是逼你在cin >>崩溃后查cin.clear(),在p[i]越界后补n+1容量,在operator+返回临时对象时确认析构时机——每一处报错,都是 C++ 编译器在给你发 debug 提示。
如果你正卡在“知道概念但写不出可运行代码”的阶段,或者想用一个真实项目验证自己对const成员函数、static生命周期、protected继承边界的理解,这份报告就是你的最小可行练兵场。它不追求炫技,但每行代码都直指 C++ 工程落地中最容易踩的坑。接下来,我会带你把这份报告里的五组源码,真正变成你本地能g++ -std=c++11编译、能 gdb 调试、能加日志验证、能安全二次开发的可执行资产。
2. 多项式类(Polynomial):动态数组 + 运算符重载的完整闭环实现
这份实验最硬核的部分,不是写个operator+,而是让整个类在任意输入、任意运算组合下不崩溃、不越界、不泄漏。原始报告里Polynomial.h和Polynomial.cpp存在多处关键缺陷:默认构造函数分配了n=0的数组却未置零、output()函数对系数为 ±1 的项处理不全、calculate()用pow(x,i)引入浮点误差且未处理x=0边界。我们先修复结构,再逐模块拆解。
2.1 目录结构与编译命令:从混乱到清晰
原始报告中文件名大小写不统一(Ploynomial.cppvsPolynomial.h)、头文件宏定义不一致(#ifndef POLYNOMIALvs#ifndef STRING_H_)、主函数分散在多个.cpp文件里。这会导致g++ main.cpp Polynomial.cpp时链接失败,或#include "Polynomial.h"找不到文件。
规范后的目录结构(推荐):
polynomial/ ├── include/ │ └── Polynomial.h // 统一宏定义:#ifndef POLYNOMIAL_H_ ├── src/ │ ├── Polynomial.cpp // 实现所有成员函数 │ └── main.cpp // 唯一入口,测试全部功能 └── CMakeLists.txt // 可选,但强烈建议关键编译命令(Linux/macOS):
# 单文件快速验证(跳过头文件依赖) g++ -std=c++11 -o poly src/Polynomial.cpp src/main.cpp # 带警告的生产级编译(必须加!) g++ -std=c++11 -Wall -Wextra -Werror -o poly src/Polynomial.cpp src/main.cpp # 运行 ./poly提示:
-Wall -Wextra -Werror是 C++ 开发的后悔药。原始代码中p[0] = '\0'(给 int 数组赋 char 值)、for (int i = n; i >= 0; i--)(i 为无符号时死循环)等隐患,开这些警告会直接报错,逼你修复。
2.2 核心类定义:补齐 const、static、友元三大要素
原始Polynomial.h缺失const修饰、static计数器未声明、operator<<未定义。修复后的头文件关键段如下:
// include/Polynomial.h #ifndef POLYNOMIAL_H_ #define POLYNOMIAL_H_ #include <iostream> #include <cmath> // 用于 calculate,但注意:应避免 pow(),见后文 #include <vector> // 更安全的替代方案,此处先保留原逻辑 class Polynomial { private: int n; // 最高次数 int* p; // 动态数组,p[i] 表示 x^i 的系数 static int count; // 静态成员:已创建的 Polynomial 对象总数 public: // 构造函数族 Polynomial(); // 默认构造:n=0, p=nullptr explicit Polynomial(int degree); // 显式构造:分配 degree+1 个 int Polynomial(const Polynomial& other); // 拷贝构造:深拷贝 ~Polynomial(); // 析构:释放 p // 输入输出(非 const,因修改对象状态) void input(); void output() const; // 关键!output 不修改对象,声明为 const // 计算(const,因只读取对象数据) double calculate(int x) const; // 返回值,而非 void + cout // 运算符重载(返回新对象,不修改 this) Polynomial operator+(const Polynomial& rhs) const; Polynomial operator-(const Polynomial& rhs) const; Polynomial operator*(const Polynomial& rhs) const; Polynomial& operator=(const Polynomial& rhs); // 赋值,返回引用 // 友元函数:支持 cout << poly friend std::ostream& operator<<(std::ostream& os, const Polynomial& poly); }; // 静态成员定义(必须在 .cpp 中,不能在头文件内) // extern int Polynomial::count; // 错误!这是声明,不是定义 // 正确做法见 2.3 节 #endif // POLYNOMIAL_H_参数说明与设计理由:
explicit Polynomial(int degree):加explicit防止隐式转换(如Polynomial p = 5;),这是 C++ 现代实践。output() const和calculate() const:明确告诉编译器和调用者“此函数不改变对象状态”,提升可读性与安全性。static int count:记录对象数量,需在.cpp中定义并初始化(int Polynomial::count = 0;),否则链接时报undefined reference。friend std::ostream& operator<<:让std::cout << p1;可用,比p1.output()更符合 C++ 习惯。
2.3 关键成员函数实现:内存安全与边界处理
原始Polynomial.cpp中input()函数对错误输入的处理不彻底,output()对x^1、x^0、系数 ±1 的格式化有遗漏,calculate()用pow()引入精度问题。以下是修复后的核心实现(src/Polynomial.cpp):
// src/Polynomial.cpp #include "include/Polynomial.h" #include <iostream> #include <iomanip> // 用于 setw,控制输出对齐(可选) #include <algorithm> // 用于 std::max // 静态成员定义(必须!) int Polynomial::count = 0; // 默认构造:n=0, p=nullptr(更安全,避免 new int[0]) Polynomial::Polynomial() : n(0), p(nullptr) { ++count; } // 显式构造:分配 degree+1 个 int,全部初始化为 0 Polynomial::Polynomial(int degree) : n(degree), p(new int[degree + 1]()) { // () 初始化为 0,避免垃圾值 ++count; } // 拷贝构造:深拷贝 Polynomial::Polynomial(const Polynomial& other) : n(other.n), p(new int[other.n + 1]) { for (int i = 0; i <= other.n; ++i) { p[i] = other.p[i]; } ++count; } // 析构:安全释放 Polynomial::~Polynomial() { delete[] p; // 注意:delete[],不是 delete --count; } // 输入:带完整错误处理 void Polynomial::input() { std::cout << "请输入多项式的最高次数 n (>=0): "; while (!(std::cin >> n) || n < 0) { std::cin.clear(); // 清除错误标志 std::cin.ignore(10000, '\n'); // 丢弃错误输入的整行 std::cout << "输入错误!n 必须是非负整数,请重新输入: "; } // 释放旧内存(如果存在) delete[] p; p = new int[n + 1](); // 分配并初始化为 0 std::cout << "请按升序输入系数(a0, a1, ..., an),空格分隔: "; for (int i = 0; i <= n; ++i) { while (!(std::cin >> p[i])) { std::cin.clear(); std::cin.ignore(10000, '\n'); std::cout << "系数 " << i << " 输入错误,请重新输入: "; } } } // 输出:严格按 -x^4-6x^3+5 格式,处理所有边界 void Polynomial::output() const { if (n == 0 && p[0] == 0) { std::cout << "0"; return; } bool firstTerm = true; // 从最高次项开始遍历 for (int i = n; i >= 0; --i) { if (p[i] == 0) continue; // 跳过系数为 0 的项 if (!firstTerm) { std::cout << (p[i] > 0 ? " + " : " - "); } else { if (p[i] < 0) std::cout << "-"; } int absCoeff = std::abs(p[i]); if (i == 0) { // 常数项:直接输出系数绝对值 std::cout << absCoeff; } else if (i == 1) { // x^1 项:输出系数(若为1则省略)+ "x" if (absCoeff == 1) { std::cout << "x"; } else { std::cout << absCoeff << "x"; } } else { // x^i 项:输出系数(若为1则省略)+ "x^" + i if (absCoeff == 1) { std::cout << "x^" << i; } else { std::cout << absCoeff << "x^" << i; } } firstTerm = false; } if (firstTerm) std::cout << "0"; // 全零多项式 } // 计算:用霍纳法(Horner's method)替代 pow(),避免浮点误差和性能问题 double Polynomial::calculate(int x) const { if (n < 0) return 0.0; if (n == 0) return static_cast<double>(p[0]); double result = static_cast<double>(p[n]); for (int i = n - 1; i >= 0; --i) { result = result * x + p[i]; } return result; } // 友元输出流重载 std::ostream& operator<<(std::ostream& os, const Polynomial& poly) { poly.output(); return os; }逻辑说明与参数详解:
input():用while (!(std::cin >> n) || n < 0)同时检查输入流状态和业务逻辑(n≥0),std::cin.ignore(10000, '\n')比while (cin.get() != '\n')更鲁棒,防止缓冲区残留。output():核心是firstTerm标志位。首项不加+/-,后续项根据系数正负决定符号;absCoeff == 1时省略系数(如x^2而非1x^2);i==0和i==1单独处理,确保+5和+3x格式正确。calculate():霍纳法是重点。原始pow(x,i)在x=10, i=10时计算10^10,不仅慢,且pow()返回double,int系数转double再乘可能丢失精度。霍纳法((a_n * x + a_{n-1}) * x + ...)全整数运算,精准高效。operator<<:作为友元,可直接访问poly.output(),保持接口一致性。
2.4 运算符重载实现:深拷贝、内存分配与异常安全
原始Operation.cpp中operator+和operator*存在严重缺陷:operator+未处理n不同导致的数组越界;operator*的嵌套循环中multiply.p[i + j]可能越界(i+j最大为n+dxs.n,但multiply.p只分配了n+dxs.n+1,索引0..n+dxs.n合法,i+j正好在此范围,但原始代码multiply.p[i + j] = multiply.p[i + j] + temp[j];未初始化multiply.p为 0,导致垃圾值累加)。
修复后的operator+和operator*(接续Polynomial.cpp):
// 加法:结果次数为 max(n, rhs.n),系数对应相加 Polynomial Polynomial::operator+(const Polynomial& rhs) const { int maxN = std::max(n, rhs.n); Polynomial result(maxN); // 将当前对象系数复制到 result for (int i = 0; i <= n && i <= maxN; ++i) { result.p[i] = p[i]; } // 将 rhs 系数加到 result for (int i = 0; i <= rhs.n && i <= maxN; ++i) { result.p[i] += rhs.p[i]; } return result; // RVO 优化,无需额外拷贝 } // 乘法:结果次数为 n + rhs.n,使用标准卷积 Polynomial Polynomial::operator*(const Polynomial& rhs) const { if (n < 0 || rhs.n < 0) return Polynomial(0); // 边界处理 int resultN = n + rhs.n; Polynomial result(resultN); // 初始化 result.p 为 0(由构造函数保证) // 卷积:result.p[k] = sum_{i+j=k} p[i] * rhs.p[j] for (int i = 0; i <= n; ++i) { for (int j = 0; j <= rhs.n; ++j) { result.p[i + j] += p[i] * rhs.p[j]; } } return result; } // 赋值运算符:经典三步法(检查自赋值、释放旧资源、深拷贝) Polynomial& Polynomial::operator=(const Polynomial& rhs) { if (this == &rhs) return *this; // 自赋值检查 delete[] p; // 释放当前资源 n = rhs.n; p = new int[n + 1]; for (int i = 0; i <= n; ++i) { p[i] = rhs.p[i]; } return *this; }关键点解析:
operator+:用std::max(n, rhs.n)确定结果次数,循环中i <= n && i <= maxN防止p[i]越界(当rhs.n > n时,i只遍历到n)。operator*:双重循环实现数学卷积,result.p[i+j] += p[i] * rhs.p[j]是核心。result构造时已用()初始化为 0,确保累加正确。operator=:三步法是铁律。原始代码缺失自赋值检查(if (this == &rhs)),若p1 = p1,先delete[] p再p[i] = rhs.p[i]会访问已释放内存,必崩。
3. String 类:手动内存管理与 RAII 思想的启蒙实践
B 部分的String类看似简单,实则是 C++ 内存管理的“照妖镜”。原始String.h中cat()函数有致命 bug:strcat(str,st.str)修改了this->str,但未检查this->str容量是否足够,导致缓冲区溢出;String::String(const char * s)未处理s为nullptr的情况;operator>>的is.get(temp,max)未检查temp是否以\0结尾。这些不是小疏忽,而是 C++ 程序员必须刻进 DNA 的安全红线。
3.1 类接口重构:从“能用”到“健壮”
原始String类缺少移动语义(C++11)、c_str()接口、operator[]下标访问,且cat()命名不达意(应为append)。我们按现代 C++ 习惯重构:
// include/String.h #ifndef STRING_H_ #define STRING_H_ #include <iostream> #include <cstring> #include <cstdlib> // for strlen, strcpy, strcat class String { private: char* str; // 指向堆内存的指针 size_t len; // 当前长度(不含 '\0') public: // 构造 String(); // 空字符串 String(const char* s); // 从 C 字符串构造 String(const String& other); // 拷贝构造 String(String&& other) noexcept; // 移动构造(C++11) // 析构 ~String(); // 赋值 String& operator=(const String& other); // 拷贝赋值 String& operator=(String&& other) noexcept; // 移动赋值 // 访问 size_t length() const { return len; } const char* c_str() const { return str; } char& operator[](size_t i) { return str[i]; } // 可修改 const char& operator[](size_t i) const { return str[i]; } // 只读 // 连接(更名 append,语义清晰) String& append(const String& other); // 友元流操作符 friend std::ostream& operator<<(std::ostream& os, const String& s); friend std::istream& operator>>(std::istream& is, String& s); }; #endif // STRING_H_设计意图:
size_t len:比int len更符合字符串长度语义(无符号,范围更大)。String(String&&)和operator=(String&&):移动语义可避免深拷贝开销,是 C++11 后必备技能。c_str():提供与 C 库交互的标准接口。append()替代cat():STLstd::string命名惯例,降低学习成本。
3.2 关键函数实现:防御式编程与异常安全
String.cpp中的核心实现必须处理所有边界:
// src/String.cpp #include "include/String.h" #include <iostream> #include <stdexcept> // 默认构造 String::String() : str(new char[1]), len(0) { str[0] = '\0'; } // C 字符串构造:处理 nullptr String::String(const char* s) : len(0), str(nullptr) { if (s == nullptr) { str = new char[1]; str[0] = '\0'; return; } len = std::strlen(s); str = new char[len + 1]; std::strcpy(str, s); } // 拷贝构造 String::String(const String& other) : len(other.len) { str = new char[len + 1]; std::strcpy(str, other.str); } // 移动构造 String::String(String&& other) noexcept : str(other.str), len(other.len) { other.str = nullptr; other.len = 0; } // 析构 String::~String() { delete[] str; } // 拷贝赋值:三步法 String& String::operator=(const String& other) { if (this == &other) return *this; delete[] str; len = other.len; str = new char[len + 1]; std::strcpy(str, other.str); return *this; } // 移动赋值 String& String::operator=(String&& other) noexcept { if (this == &other) return *this; delete[] str; str = other.str; len = other.len; other.str = nullptr; other.len = 0; return *this; } // 连接:先扩容,再 strcat String& String::append(const String& other) { if (other.len == 0) return *this; // 空字符串,直接返回 size_t newLen = len + other.len; char* newStr = new char[newLen + 1]; // 复制原字符串 std::strcpy(newStr, str); // 追加 other 字符串 std::strcat(newStr, other.str); delete[] str; str = newStr; len = newLen; return *this; } // 友元输出 std::ostream& operator<<(std::ostream& os, const String& s) { os << s.str; return os; } // 友元输入:安全读取,避免缓冲区溢出 std::istream& operator>>(std::istream& is, String& s) { const size_t MAX_INPUT = 1024; char buffer[MAX_INPUT]; is.getline(buffer, MAX_INPUT); // 读取一行,自动处理 \n if (is.gcount() == MAX_INPUT - 1 && is.peek() != '\n') { // 输入超长,清空剩余 is.clear(); is.ignore(10000, '\n'); } s = String(buffer); // 调用拷贝赋值 return is; }血泪经验总结:
String(const char* s):必须检查s == nullptr。C++ 中向strlen(nullptr)传参是未定义行为(UB),程序可能立即崩溃。append():原始cat()直接strcat(str,st.str)是定时炸弹。正确做法是new一块足够大的新内存,strcpy+strcat,再delete[]旧内存。这是 RAII(Resource Acquisition Is Initialization)思想的朴素体现:资源生命周期与对象绑定。operator>>:用getline()替代get(),并检查gcount()判断是否读满缓冲区,再ignore()清理,杜绝输入攻击。
3.3 主函数测试:验证所有路径
main.cpp应覆盖所有构造、赋值、连接场景:
// src/main_string.cpp #include "include/String.h" #include <iostream> int main() { // 测试 1:字面量构造 String s = "吉林大学"; std::cout << "s = \"" << s << "\", len = " << s.length() << std::endl; // 测试 2:拷贝构造 String t = s; std::cout << "t = \"" << t << "\" (copy of s)" << std::endl; // 测试 3:默认构造 + 赋值 String m; m = t; std::cout << "m = \"" << m << "\" (assigned from t)" << std::endl; // 测试 4:append 连接 String a = "软件学院"; m.append(a); std::cout << "m.append(a) => \"" << m << "\"" << std::endl; // 测试 5:移动语义(C++11) String tmp = "临时对象"; String moved = std::move(tmp); std::cout << "moved = \"" << moved << "\", tmp.len = " << tmp.length() << std::endl; // 测试 6:输入 std::cout << "请输入新字符串: "; String userInput; std::cin >> userInput; std::cout << "userInput = \"" << userInput << "\"" << std::endl; return 0; }编译运行:
g++ -std=c++11 -Wall -Wextra -Werror -o string src/String.cpp src/main_string.cpp ./string4. 继承体系(Camera/Chip)与多态工资系统:访问控制与抽象基类的实战
C 和 D 部分是面向对象设计的“高阶考场”。C 部分通过Camera→Chip→Car_camera_*的三级继承,强制你理解public/protected/private继承对派生类接口的塑造力;D 部分用Employee抽象基类和四类雇员,让你亲手写出第一个真正的多态系统。原始代码中Car_camera_two的options()调用Chip::options()会失败(protected成员在类外不可见),D 部分的SalariedEmployee多重继承Employee和Birthdate导致菱形继承歧义,且CalculateSalary()未声明为virtual,破坏多态。
4.1 继承实验:public/protected/private 的访问边界可视化
原始Camera.cpp中Car_camera_two(protected继承)试图在main()中调用b.options(),但options()在Chip中是public,protected继承后在Car_camera_two外部不可见。我们必须用类内函数桥接:
// include/Camera.h #ifndef CAMERA_H_ #define CAMERA_H_ #include <iostream> #include <string> class Camera { protected: // 关键!take_photo 改为 protected,供派生类使用 virtual void take_photo() { std::cout << "摄像功能启动!" << std::endl; } public: void image_set() { std::cout << "图像质量设定功能启动!" << std::endl; } virtual ~Camera() = default; // 虚析构,确保多态删除安全 }; class Chip : public Camera { // Chip 公共继承 Camera protected: bool menu; double speed; public: Chip() : menu(false), speed(0.0) {} void options() { std::cout << "进入操作菜单功能,请选择:" << std::endl; } void show() const { std::cout << "当前车速为:" << speed << std::endl; } void Resetmenu(bool v) { menu = v; } }; // 三种继承方式的行车记录仪 class Car_camera_one : public Chip { // public 继承:所有 public 成员对外可见 public: void open() { std::cout << "行车记录仪启动!" << std::endl; } void close() { std::cout << "行车记录仪关闭!Bye!" << std::endl; } // 可直接使用 Chip::options() 和 Camera::image_set() }; class Car_camera_two : protected Chip { // protected 继承:Chip 的 public 变成 protected public: void open() { std::cout << "行车记录仪启动!" << std::endl; } void close() { std::cout << "行车记录仪关闭!Bye!" << std::endl; } // 提供 public 接口来暴露需要的功能 void use_options() { options(); } // 在类内调用,合法 void use_image_set() { image_set(); } // Camera::image_set() 是 public,protected 继承后仍为 protected,但可在派生类内调用 }; class Car_camera_three : private Chip { // private 继承:所有成员变为 private public: void open() { std::cout << "行车记录仪启动!" << std::endl; } void close() { std::cout << "行车记录仪关闭!Bye!" << std::endl; } void use_options() { options(); } // 同样,类内可调用 }; #endif // CAMERA_H_关键修正点:
Camera::take_photo()改为protected:public方法被protected/private继承后,外部无法调用,但派生类内部需要它,所以设为protected。Car_camera_two::use_options():protected继承后,options()对main()不可见,但Car_camera_two类内可调用,因此提供use_options()作为门面。virtual ~Camera() = default:虚析构是多态删除的基石。若delete ptr_to_Chip,没有虚析构,Camera部分不会被正确析构。
4.2 工资系统:抽象基类、多态与容器管理
D 部分原始代码最大问题是Employee未声明为抽象基类(virtual函数未 =0),且SalariedEmployee等类未重写CalculateSalary()为virtual,导致vector<Employee*>中调用的是基类空实现。此外,Birthdate应作为Employee的成员,而非多重继承,避免菱形问题。
重构后的Employee体系(include/Employee.h):
// include/Employee.h #ifndef EMPLOYEE_H_ #define EMPLOYEE_H_ #include <iostream> #include <string> #include <vector> #include <memory> // 用智能指针管理 // 日期类,作为 Employee 成员 class Date { public: int year, month, day; Date(int y = 1970, int m = 1, int d = 1) : year(y), month(m), day(d) {} bool isBirthdayThisMonth(int currentMonth) const { return month == currentMonth; } }; // 抽象基类 class Employee { protected: std::string m_name; int m_id; Date birthDate; public: Employee(const std::string& name, int id, const Date& bd) : m_name(name), m_id(id), birthDate(bd) {} // 纯虚函数,强制派生类实现 virtual double CalculateSalary(int currentMonth) = 0; virtual void Output(int currentMonth) = 0; virtual ~Employee() = default; // 虚析构 }; // 周薪雇员 class SalariedEmployee : public Employee { private: double weeklySalary; public: SalariedEmployee(const std::string& name, int id, const Date& bd, double salary) : Employee(name, id, bd), weeklySalary(salary) {} double CalculateSalary(int currentMonth) override { double base = weeklySalary; if (birthDate.isBirthdayThisMonth(currentMonth)) { base += 100.0; } return base; } void Output(int currentMonth) override { std::cout << "员工类型: 周薪雇员 | 姓名: " << m_name << " | 工号: " << m_id << " | 工资: " << CalculateSalary(currentMonth) << std::endl; } }; // 时薪雇员(简化版,仅示意多态) class HourlyEmployee : public Employee { private: double hourlyWage; int hoursWorked; public: HourlyEmployee(const std::string& name, int id, const Date& bd, double wage, int hours) : Employee(name, id, bd), hourlyWage(wage), hoursWorked(hours) {} double CalculateSalary(int currentMonth) override { double base = (hoursWorked <= 40) ? hoursWorked * hourlyWage : 40 * hourlyWage + (hoursWorked - 40) * hourlyWage * 1.5; if (birthDate.isBirthdayThisMonth(currentMonth)) { base += 100.0; } return base; } void Output(int currentMonth) override { std::cout << "员工类型: 时薪雇员 | 姓名: " << m_name << " | 工号: " << m_id << " | 工资: " << CalculateSalary(currentMonth) << std::endl; } }; // 容器管理类 class Company { private: std::vector<std::unique_ptr<Employee>> employees; public: void addEmployee(std::unique_ptr<Employee> emp) { employees.push_back(std::move(emp)); } void payRoll(int currentMonth) { std::cout << "\n=== 本月工资发放 ===" << std::endl; for (const auto& emp : employees) { emp->Output(currentMonth); } } }; #endif // EMPLOYEE_H_核心改进:
Employee是纯抽象类:CalculateSalary()和Output()均为=0,无法实例化。override关键字:显
本文还有配套的精品资源,点击获取