实验五

实验5 多态

说明

一、实验目的

  • 知道什么是类模板,会正确定义和实例化
  • 理解运算符重载机制,会编写运算符函数,理解编译器如何将表达式转换为对运算符函数的调用
  • 知道什么是抽象类,会正确定义和使用
  • 基于问题场景,能合理使用继承、虚函数、抽象类实现接口继承与运行时多态

二、实验准备

系统浏览/复习以下教材章节:

  • 继承:解决的问题场景、定义和用法(第7章)
  • 多态:概念、分类、典型应用场景和用法(第8章)
  • 类模板(9.1.2节)

三、实验内容

1. 实验任务1

说明

验证性实验。综合应用组合、继承、多态实现出版物的分类管理。运行、理解代码,回答问题。

  • 问题场景描述
    模拟出版物的分类管理。抽象设计后,继承关系如下:
    image

  • 代码组织

    • publisher.hpp 类Publisher及其派生类Book, Film, Music声明
    • publisher.cpp 类Publisher及其派生类Book, Film,Music实现
    • task1.cpp 测试模块+main
代码
publisher.hpp
pragma once

include<string>

//发行/出版物类:Publisher(抽象类)
class Publisher {
public:
    Publisher(const std::string& name_ = ""); //构造函数
    virtual ~Publisher() = default;

public:
    virtual void publish() const = 0; //纯虚函数,作为接口继承
    virtual void use() const = 0;     //纯虚函数,作为接口继承

protected:
    std::string name; //发行/出版物名称
};

//图书类:Book
class Book: public Publisher {
public:
    Book(const std::string& name_ = "", const std::string& author_ = ""); //构造函数

public:
    void publish() const override; //接口
    void use() const override;     //接口

private:
    std::string author; //作者
};

//电影类:Film
class Film: public Publisher {
public:
    Film(const std::string& name_ = "", const std::string& director_ = ""); //构造函数

public:
    void publish() const override; //接口
    void use() const override;     //接口

private:
    std::string director; //导演
};

//音乐类:Music
class Music: public Publisher {
public:
    Music(const std::string& name_ = "", const std::string& artist_ = "");

public:
    void publish() const override; //接口
    void use() const override;     //接口

private:
    std::string artist; //音乐艺术家名称
};
publisher.cpp
include<iostream>

include<string>

include "publisher.hpp"

// Publisher类:实现
Publisher::Publisher(const std::string& name_): name{name_} {
}

// Book类: 实现
Book::Book(const std::string &name_ , const std::string &author_ ): Publisher{name_}, author{author_} {
}

void Book::publish() const {
    std::cout << "Publishing book《" << name << "》 by " << author << '\n';
}

void Book::use() const {
    std::cout << "Reading book 《" << name << "》 by " << author << '\n';
}


// Film类:实现
Film::Film(const std::string &name_, const std::string &director_):Publisher{name_},director{director_} {
}

void Film::publish() const {
    std::cout << "Publishing film <" << name << "> directed by " << director << '\n';
}

void Film::use() const {
    std::cout << "Watching film <" << name << "> directed by " << director << '\n';
}


// Music类:实现
Music::Music(const std::string &name_, const std::string &artist_): Publisher{name_}, artist{artist_} {
}

void Music::publish() const {
    std::cout << "Publishing music <" << name << "> by " << artist << '\n';
}

void Music::use() const {
    std::cout << "Listening to music <" << name << "> by " << artist << '\n';
}
task1.cpp
include <vector>
include <memory>
include "publisher.hpp"

void test1() {
    std::vector<Publisher*> v;

    v.push_back(new Book("Harry Potter", "J.K. Rowling"));
    v.push_back(new Film("The Godfather", "Francis Ford Coppola"));
    v.push_back(new Music("Blowing in the wind", "Bob Dylan"));

    for(Publisher* ptr: v) {
        ptr->publish();
        ptr->use();
        std::cout << '\n';
        delete ptr;
    }
}

void test2() {
    std::vector<std::unique_ptr<Publisher>> v;

//devc++不支持 
//    v.push_back(std::make_unique<Book>("Harry Potter", "J.K. Rowling"));
//    v.push_back(std::make_unique<Film>("The Godfather", "Francis Ford Coppola"));
//    v.push_back(std::make_unique<Music>("Blowing in the wind", "Bob Dylan"));

//旧写法 
	v.push_back(std::unique_ptr<Book>(new Book("Harry Potter", "J.K. Rowling")));
    v.push_back(std::unique_ptr<Film>(new Film("The Godfather", "Francis Ford Coppola")));
    v.push_back(std::unique_ptr<Music>(new Music("Blowing in the wind", "Bob Dylan")));

    for(const auto& ptr: v) {
        ptr->publish();
        ptr->use();
        std::cout << '\n';
    }
}

void test3() {
    Book book("A Philosophy of Software Design", "John ousterhout");
    book.publish();
    book.use();
}

int main() {
    std::cout << "运行时多态:纯虚函数、抽象类\n";

    std::cout << "\n测试1:使用原始指针\n";
    test1();

    std::cout << "\n测试2:使用智能指针\n";
    test2();

    std::cout << "\n测试3:直接使用类\n";
    test3();
}
运行结果

image

回答问题

问题1:抽象类机制
(1) 是什么决定了 Publisher是抽象类?用一句话说明,并指出代码中的具体依据。
(2) 如果在 main.cpp里直接写 Publisher p; 能否编译通过?为什么?

答:(1)Publisher里的两个纯虚函数决定了这是个抽象类。virtual void publish() const = 0;virtual void use() const = 0;(2)不能,抽象类无法实例化,因为纯虚函数没有具体实现。

问题2:纯虚函数与接口继承
(1) BookFilmMusic必须实现哪两个函数才能通过编译?请写出其完整函数声明
(2) 在 publisher.cpp的 Film类实现中,把两个成员函数实现里的const去掉(保持函数体不变),重新编译,报错信息是什么?

答:(1)publish()use(),完整函数声明代码在上面代码中。(2)错误如下:

[Error] prototype for 'void Film::publish()' does not match any in class 'Film' 
[Error] candidate is: virtual void Film::publish() const 
[Error] prototype for 'void Music::publish()' does not match any in class 'Music' 
[Error] candidate is: virtual void Music::publish() const 

就是找不到对应实现的错误。

问题3:运行时多态与虚析构
(1) 在 test1()里, for(Publisher* ptr: v)ptr的声明类型是什么?
(2) 当循环执行到 ptr->publish(); 时,ptr实际指向的对象类型分别有哪些?(按循环顺序写出)
(3) 基类 Publisher的析构函数为何声明为 virtual?若删除 virtual,执行 delete ptr; 会出现什么问题?

答(1)声明类型就是Publisher*。(2)Book*, Film*, Music*(3)需要子类实现析构函数,因为多态调用时,使用基类指针如果不是virtual的话不会调用子类的析构函数,用virtual后可以导向子类析构函数,防止内存泄露。

2. 实验任务2

说明

验证性实验。综合应用运算符重载、组合、标准库实现图书销售统计。

  • 问题场景描述
    模拟出版行业图书销售统计,按指定关键字做销量统计、排序。

  • 代码组织

    • book.hpp 图书描述信息类Book声明
    • book.cpp 图书描述信息类Book实现
    • booksale.hpp 图书销售记录类BookSale声明
    • booksale.cpp 图书销售记录类BookSale实现
    • task2.cpp 测试模块+ main
代码
book.hpp
pragma once
include<string>

//图书描述信息类Book:声明
class Book {
public:
    Book(const std::string& name_,
         const std::string& author_,
         const std::string& translator_,
         const std::string& isbn_,
         double price_);

    friend std::ostream& operator<<(std::ostream& out, const Book& book);

private:
    std::string name;       //书名
    std::string author;     //作者
    std::string translator; //译者
    std::string isbn;       //isbn号
    double price;           //定价
};
book.cpp
cpp
include <iomanip>
include <iostream>
include <string>
include "book.hpp"

//图书描述信息类Book:实现
Book::Book(const std::string& name_,
           const std::string& author_,
           const std::string& translator_,
           const std::string& isbn_,
           double price_): name{name_}, author{author_}, translator{translator_},
                          isbn{isbn_}, price{price_} {
}

//运算符<<重载实现
std::ostream& operator<<(std::ostream& out, const Book& book) {
    using std::left;
    using std::setw;

    out << left;
    out << setw(15) << "书名:" << book.name << '\n'
        << setw(15) << "作者:" << book.author << '\n'
        << setw(15) << "译者:" << book.translator << '\n'
        << setw(15) << "ISBN:" << book.isbn << '\n'
        << setw(15) << "定价:" << book.price;

    return out;
}
booksale.hpp
pragma once
include <string>
include "book.hpp"

//图书销售记录类BookSales:声明
class BookSale {
public:
    BookSale(const Book& rb_, double sales_price_, int sales_amount_);
    int get_amount() const;       //返回销售数量
    double get_revenue() const;   //返回营收

    friend std::ostream& operator<<(std::ostream& out, const BookSale& item);

private:
    Book rb;
    double sales_price;  //售价
    int sales_amount;    //销售数量
};
booksale.cpp
include <iomanip>
include <iostream>
include <string>
include "booksale.hpp"

//图书销售记录类BookSales:实现
BookSale::BookSale(const Book& rb_,
                   double sales_price_,
                   int sales_amount_): rb{rb_}, sales_price{sales_price_},
                                      sales_amount{sales_amount_} {
}

int BookSale::get_amount() const {
    return sales_amount;
}

double BookSale::get_revenue() const {
    return sales_amount * sales_price;
}

//运算符<<重载实现
std::ostream& operator<<(std::ostream& out, const BookSale& item) {
    using std::left;
    using std::setw;

    out << left;
    out << item.rb << '\n'
        << setw(15) << "售价:" << item.sales_price << '\n'
        << setw(15) << "销售数量:" << item.sales_amount << '\n'
        << setw(15) << "营收:" << item.get_revenue();

    return out;
}
task2.cpp
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "booksale.hpp"

// 按图书销售数量比较
bool compare_by_amount(const BookSale &x1, const BookSale &x2) {
    return x1.get_amount() > x2.get_amount();
}

void test() {
    using std::cin;
    using std::cout;
    using std::getline;
    using std::sort;
    using std::string;
    using std::vector;
    using std::ws;

    vector<BookSale> sales_records;         // 图书销售记录表

    int books_number;
    cout << "录入图书数量: ";
    cin >> books_number;

    cout << "录入图书销售记录\n";
    for(int i = 0; i < books_number; ++i) {
        string name, author, translator, isbn;
        double price;
        cout << string(20, '-') << "第" << i+1 << "本图书信息录入" << string(20, '-') << '\n';
        cout << "录入书名: "; getline(cin>>ws, name);
        cout << "录入作者: "; getline(cin>>ws, author);
        cout << "录入译者: "; getline(cin>>ws, translator);
        cout << "录入isbn: "; getline(cin>>ws, isbn);
        cout << "录入定价: "; cin >> price;

        Book book(name, author, translator, isbn, price);

        double sales_price;
        int sales_amount;

        cout << "录入售价: "; cin >> sales_price;
        cout << "录入销售数量: "; cin >> sales_amount;

        BookSale record(book, sales_price, sales_amount);
        sales_records.push_back(record);
    }


    // 按销售册数排序
    sort(sales_records.begin(), sales_records.end(), compare_by_amount);

    // 按销售册数降序输出图书销售信息
    cout << string(20, '=') <<  "图书销售统计" << string(20, '=') << '\n';
    for(auto &record: sales_records) {
        cout << record << '\n';
        cout << string(40, '-') << '\n';
    }
}

int main() {
    test();
}
运行结果

image

回答问题

问题1:重载运算符<<
(1) 找出运算符<<被重载了几处?分别用于什么类型?
(2) 找出使用重载<<输出对象的代码,写在下面。

答:(1)两处,重载了BookBookSale的输出流。

(2)

std::ostream& operator<<(std::ostream& out, const Book& book) {
    using std::left;
    using std::setw;

    out << left;
    out << setw(15) << "书名:" << book.name << '\n'
        << setw(15) << "作者:" << book.author << '\n'
        << setw(15) << "译者:" << book.translator << '\n'
        << setw(15) << "ISBN:" << book.isbn << '\n'
        << setw(15) << "定价:" << book.price;
    
    return out;

}

std::ostream& operator<<(std::ostream& out, const BookSale& item) {
    using std::left;
    using std::setw;

    out << left;
    out << item.rb << '\n'
        << setw(15) << "售价:" << item.sales_price << '\n'
        << setw(15) << "销售数量:" << item.sales_amount << '\n'
        << setw(15) << "营收:" << item.get_revenue();
    
    return out;

}

问题2:图书销售统计
(1) 图书销售记录"按销售数量降序排序",代码是如何实现的?
(2) 拓展(选答*):如果使用lambda表达式,如何实现"按销售数量降序排序"?

答:(1)sort(sales_lst.begin(), sales_lst.end(), compare_by_amount);,其中compare_by_amount这个函数指针指向

bool compare_by_amount(const BookSale& x1, const BookSale& x2) {
    return x1.get_amount() > x2.get_amount();
}

(2)可以直接用

sort(sales_lst.begin(), sales_lst.end(), [](const BookSale& x1, const BookSale& x2)->bool{
	return x1.get_amount() > x2.get_amount();
});

或者lambda省略箭头和返回值类型

[](const BookSale& x1, const BookSale& x2){
	return x1.get_amount() > x2.get_amount();
}

3. 实验任务3

说明

验证性实验:类模板定义和使用。阅读、理解代码,结合运行回答问题。

  • 问题场景描述
    类A和类B除数据成员类型不同,其它都相同。类定义存在相似性把类型参数化,让类的抽象设计更通用。

  • 代码组织

    • task3_1.cpp 类A定义+类B定义+测试模块+ main
    • task3_2.cpp 类模板X定义+测试模块+ main
task3_1.cpp
include <iostream>

//类A的定义
class A {
public:
    A(int x0, int y0);
    void display() const;

private:
    int x, y;
};

A::A(int x0, int y0): x{x0}, y{y0} {
}

void A::display() const {
    std::cout << x << "," << y << '\n';
}

//类B的定义
class B {
public:
    B(double x0, double y0);
    void display() const;

private:
    double x, y;
};

B::B(double x0, double y0): x{x0}, y{y0} {
}

void B::display() const {
    std::cout << x << "," << y << '\n';
}

void test() {
    std::cout << "测试类A:" << '\n';
    A a(3, 4);
    a.display();

    std::cout << "\n测试类B:" << '\n';
    B b(3.2, 5.6);
    b.display();
}

int main() {
    test();
}
说明: 观察代码可见,类A与类B仅数据类型不同,其余代码完全一致。 类模板可将这些"相同部分"抽象成一份定义,把类型作为参数,实现通用化。
task3_2.cpp
include <iostream>
include <string>

//定义类模板
template<typename T>
class X {
public:
    X(T x0, T y0);
    void display();

private:
    T x, y;
};

template<typename T>
X<T>::X(T x0, T y0): x{x0}, y{y0} {
}

template<typename T>
void X<T>::display() {
    std::cout << x << "," << y << '\n';
}

void test() {
    std::cout << "测试1:用int实例化类模板X" << '\n';
    X<int> x1(3, 4);
    x1.display();

    std::cout << "\n测试2:用double实例化类模板X" << '\n';
    X<double> x2(3.2, 5.6);
    x2.display();

    std::cout << "\n测试3:用string实例化类模板X" << '\n';
    X<std::string> x3("hello", "oop");
    x3.display();
}

int main() {
    test();
}
运行结果

task3_1
image
task3_2
image

说明:

  • 类模板用 template<typename T> 把"类型"抽象成参数,一份代码可生成多个具体类。
  • 在类外实现成员函数时,必须加模板头并写完整类名 X<T>
  • 使用时要先"实例化": X<int> x1(3,4);,编译器据此生成真正的类。
  • 实例化类型必须支持模板内所有操作(如<<),否则编译报错。
  • 理解类模板后,再看标准库用法 complex<double>vector<int>——只是现成模板的实例化而已。

4. 实验任务4

说明

设计性实验。综合应用继承、多态,模拟简单机器宠物。

  • 问题场景描述
    模拟机器宠物,抽象后,继承关系如下:
    (此处应有继承关系图)

  • 设计要求

    • 定义抽象类:机器宠物类 MachinePet
      每个机器宠物包含如下成员:
      • 数据:昵称(nickname)
      • 接口要求:
        • 构造函数:用字符串初始化昵称
        • get_nickname():供外部获取昵称
        • talk():返回叫声,须支持运行时多态(由派生类各自实现)。
    • 定义电子宠物猫类PetCat
      • 公有继承自 MachinePet
      • 无新增数据
      • 构造函数:用字符串初始化昵称
      • 实现talk()返回猫叫声
    • 定义电子宠物狗类PetDog
      • 公有继承自 MachinePet
      • 无新增数据
      • 构造函数:用字符串初始化昵称
      • 实现talk()返回狗叫声
  • 代码组织

    • pet.hpp 机器宠物抽象类 MachinePet、宠物猫类 PetCat、宠物狗类 PetDog定义
    • task4.cpp 测试模块+ main

测试代码task4.cpp已给出,补足pets.hpp。

代码
task4.cpp
#include <iostream>
#include <memory>
#include <vector>
#include "pet.hpp"

void test1() {
    std::vector<MachinePet *> pets;

    pets.push_back(new PetCat("miku"));
    pets.push_back(new PetDog("da huang"));

    for(MachinePet *ptr: pets) {
        std::cout << ptr->get_nickname() << " says " << ptr->talk() << '\n';
        delete ptr;  // 须手动释放资源
    }   
}

void test2() {
    std::vector<std::unique_ptr<MachinePet>> pets;

//    pets.push_back(std::make_unique<PetCat>("miku"));
//    pets.push_back(std::make_unique<PetDog>("da huang"));

	
    pets.push_back(std::unique_ptr<PetCat>(new PetCat("miku")));
    pets.push_back(std::unique_ptr<PetDog>(new PetDog("da huang")));


    for(auto const &ptr: pets)
        std::cout << ptr->get_nickname() << " says " << ptr->talk() << '\n';
}

void test3() {
    // MachinePet pet("little cutie");   // 编译报错:无法定义抽象类对象

    const PetCat cat("miku");
    std::cout << cat.get_nickname() << " says " << cat.talk() << '\n';

    const PetDog dog("da huang");
    std::cout << dog.get_nickname() << " says " << dog.talk() << '\n';
}

int main() {
    std::cout << "测试1: 使用原始指针\n";
    test1();

    std::cout << "\n测试2: 使用智能指针\n";
    test2();

    std::cout << "\n测试3: 直接使用类\n";
    test3();
}
pet.hpp
#ifndef MACHINE_PET_HEAD
#define MACHINE_PET_HEAD
#include <vector>
#include <string>
#include <iostream>
class MachinePet{
protected:
	std::string nickname;

public:
	MachinePet(const std::string& _nickname)
	:nickname(_nickname){ }
	virtual ~MachinePet() = default;

public:
	const std::string get_nickname() const{return nickname;}
	virtual std::string talk() const = 0;
};

class PetCat final: public MachinePet{
public:
	PetCat(const std::string& _n)
	:MachinePet(_n){ }
	~PetCat() = default;

public:
	std::string talk()const  override{
		return "喵";
	}
};

class PetDog final: public MachinePet{
public:
	PetDog(const std::string& _n)
	:MachinePet(_n){ }
	~PetDog() = default;

public:
	std::string talk()const  override{
		return "汪"; 
	}
};
#endif
运行结果

image

Tips:
编写机器宠物抽象类MachinePet、宠物猫类PetCat、宠物狗类PetDog定义时,如不熟练,参照任务1。

注意点:

  • 抽象类定义:若test1出现delete ptr;相关崩溃或抽象类无法实例化的报错,请复习纯虚函数和虚析构规则。
  • 派生类构造函数初始化列表写法:昵称须提供给基类
  • 拓展*:电子宠物猫/狗的talk()实现,如希望叫声效果拟真,可查阅网络,使用支持.midi/.wav等音频播放的工具函数

(最后一点win32库中的PlaySound可以简单实现(但是只能单个播放,就是没法同时播放多个),mciSendString可以同时播放,但是效率稍低。其他方法可以用Directx里的DirectSound或者第三方库SDL_Mixer实现更好的控制。)

5. 实验任务5

说明

设计性实验。综合应用运算符重载、类模板实现编译时多态。

  • 问题场景描述
    自定义简化版类模板Complex,实现类似C++标准库类模板complex,支持对类型的参数化。具体要求如下:

    • 支持实例化类模板与各种构造
      complex<double> c1; Complex<double> c2(1.0, 2.0); Comlex<double> c3(c2);
    • 提供接口 get_real(), get_image() 返回实部和虚部
    • 对类模板重载运算符,支持如下操作: c1 += c2, c1 + c2, c1 == c2, cin >> c1 >> c2, cout << c1 << c2
  • 代码组织

    • Complex.hpp 类模板Complex定义
    • task5.cpp 测试模块+main

测试代码task5.cpp已给出。根据测试代码,补足类模板Complex定义,使代码运行后满足预期截图效果。

代码
task5.cpp
include <iostream>
include "Complex.hpp"

void test1() {
    using std::cout;
    using std::boolalpha;

    Complex<int> c1(2, -5), c2(c1);

    cout << "c1 = " << c1 << '\n';
    cout << "c2 = " << c2 << '\n';
    cout << "c1 + c2 = " << c1 + c2 << '\n';

    c1 += c2;
    cout << "c1 = " << c1 << '\n';
    cout << boolalpha << (c1 == c2) << '\n';
}

void test2() {
    using std::cin;
    using std::cout;

    Complex<double> c1, c2;
    cout << "Enter c1 and c2: ";
    cin >> c1 >> c2;
    cout << "c1 = " << c1 << '\n';
    cout << "c2 = " << c2 << '\n';

    const Complex<double> c3(c1);
    cout << "c3.real = " << c3.get_real() << '\n';
    cout << "c3.imag = " << c3.get_imag() << '\n';
}

int main() {
    std::cout << "自定义类模板Complex测试1: \n";
    test1();

    std::cout << "\n自定义类模板Complex测试2: \n";
    test2();
}
Complex.hpp
#ifndef COMPLEX_HEAD
#define COMPLEX_HEAD
#include <iostream>
#include <string> 
#include <cmath>
template<class T>
class Complex {
private:
	T real;
	T imag;

public:
	Complex(T r = 0.0, T i = 0.0)
		:real(r), imag(i) {
	}
	~Complex() = default;

public:
	const T get_real() const { return real; }
	const T get_imag() const { return imag; }

public:
	Complex operator+(const Complex& o) {
		return Complex(this->real + o.real, this->imag + o.imag);
	}

	bool operator==(const Complex& o) {
		return (this->real == o.real) && (this->imag == o.imag);
	}
	
	friend std::istream& operator>>(std::istream& input, Complex& o) {
		input >> o.real >> o.imag;
		return input;
	}
	
	friend std::ostream& operator<<(std::ostream& out, const Complex& c) {
		std::string r;
		std::string i;
	
		if (c.real == 0) {
			r = "";
		}
		else {
			r = std::to_string(c.real);
		}
	
		if (c.imag == 0) {
			i = "";
		}
		else {
			i = (c.imag > 0 ? "+ " : "- ") + std::to_string(abs(c.imag)) + 'i';
		}
	
		out << r << ' ' << i << std::endl;
		return out;
	}
	
	Complex& operator+=(const Complex& o) {
		this->real += o.real;
		this->imag += o.imag;
	
		return *this;
	}

};
#endif
运行结果

image

四、实验结论

任务1

  • 抽象类通过纯虚函数实现,不能直接实例化
  • 派生类必须实现基类中的所有纯虚函数
  • 通过基类指针/引用可以调用派生类的虚函数,实现运行时多态
  • 多态环境下必须使用虚析构函数以确保资源正确释放

任务2

  • 运算符重载扩展了类的功能,使其使用更自然
  • 通过重载<<运算符,可以直接输出自定义类型对象
  • 标准库算法sort与自定义比较函数配合,可以灵活排序
  • 组合关系可以复用已有类的功能

任务3

  • 类模板可以参数化类型,提高代码复用性
  • 类模板需要实例化后才能使用
  • 编译器会根据实例化类型生成具体的类
  • 类模板的成员函数在类外实现时需要额外的模板声明

任务4

  • 合理设计抽象基类MachinePet,定义统一接口
  • 通过纯虚函数talk()强制派生类实现特定行为
  • 利用多态性,可以通过基类指针统一管理不同类型的宠物
  • 智能指针(unique_ptr)可以自动管理内存,避免内存泄漏

任务5

  • 类模板Complex支持多种数值类型
  • 运算符重载使复数运算更直观自然
  • 流运算符重载支持直接输入输出
  • 成员函数实现基本的复数运算功能
  • 注意处理输出格式,特别是虚部的符号表示

五、实验总结

通过本次实验,我深入理解了:

  • 编译时多态:通过函数重载、运算符重载和类模板实现
  • 运行时多态:通过继承、虚函数和抽象类实现
  • 抽象类是定义接口和规范的重要工具,强制派生类实现特定的功能
  • 运算符重载可以让类的用起来更方便
  • 类模板提供了强大的代码复用能力
  • 智能指针可以自动释放资源,避免忘记delete
posted @ 2025-12-10 16:58  cuupe  阅读(17)  评论(1)    收藏  举报