实验5 多态

实验任务一:

源代码:

 

 1 #pragma once
 2 
 3 #include <string>
 4 
 5 // 发行/出版物类:Publisher (抽象类)
 6 class Publisher {
 7 public:
 8     Publisher(const std::string& name_ = "");// 构造函数
 9     virtual ~Publisher() = default;
10 public:
11     virtual void publish() const = 0;    // 纯虚函数,作为接口继承
12     virtual void use() const = 0;        // 纯虚函数,作为接口继承
13 protected:
14     std::string name;    // 发行/出版物名称
15 };
16 
17 // 图书类: Book
18 class Book : public Publisher {
19 public:
20     Book(const std::string& name_ = "", const std::string& author_ = "");  // 构造函数
21 public:
22     void publish() const override;    // 接口
23     void use() const override;    // 接口
24 private:
25     std::string author;        // 作者
26 };
27 
28 
29 
30 // 电影类: Film
31 class Film : public Publisher {
32 public:
33     Film(const std::string& name_ = "", const std::string& director_ = "");   // 构造函数
34 public:
35     void publish() const override;// 接口
36     void use() const override;// 接口   
37 private:
38     std::string director;// 导演
39     
40              
41     
42 };
43 // 音乐类:Music
44 class Music : public Publisher {
45 public:
46     Music(const std::string& name_ = "", const std::string& artist_ = "");
47 public:
48     void publish() const override;// 接口
49     void use() const override;// 接口
50 private:
51     std::string artist;// 音乐艺术家名称
52     
53 };
publisher.hpp

 

 1 #include <iostream>
 2 #include <string>
 3 #include "publisher.hpp"
 4 // Publisher类:实现
 5 Publisher::Publisher(const std::string& name_) : name{ name_ } {
 6 }
 7 // Book类: 实现
 8 Book::Book(const std::string& name_, const std::string& author_) : Publisher{ name_ },
 9 author{ author_ } {
10 }
11 void Book::publish() const {
12     std::cout << "Publishing book《" << name << "》 by " << author << '\n';
13 }
14 void Book::use() const {
15     std::cout << "Reading book 《" << name << "》 by " << author << '\n';
16 }
17 // Film类:实现
18 Film::Film(const std::string& name_, const std::string
19     & director_) :Publisher{ name_ }, director{ director_ } {
20 }
21 void Film::publish() const {
22     std::cout << "Publishing film <" << name << "> directed by " << director << '\n';
23 }
24 void Film::use() const {
25     std::cout << "Watching film <" << name << "> directed by " << director << '\n';
26 }
27 // Music类:实现
28 Music::Music(const std::string& name_, const std::string& artist_) : Publisher{ name_ },
29 artist{ artist_ } {
30 }
31 void Music::publish() const {
32     std::cout << "Publishing music <" << name << "> by " << artist << '\n';
33 }
34 void Music::use() const {
35     std::cout << "Listening to music <" << name << "> by " << artist << '\n';
36 }
publisher.cpp
 1 #include <memory>
 2 #include <iostream>
 3 #include <vector>
 4 #include "publisher.hpp"
 5 void test1() {
 6     std::vector<Publisher*> v;
 7     v.push_back(new Book("Harry Potter", "J.K. Rowling"));
 8     v.push_back(new Film("The Godfather", "Francis Ford Coppola"));
 9     v.push_back(new Music("Blowing in the wind", "Bob Dylan"));
10     for (Publisher* ptr : v) {
11         ptr->publish();
12         ptr->use();
13         std::cout << '\n';
14         delete ptr;
15     }
16 }
17 void test2() {
18     std::vector<std::unique_ptr<Publisher>> v;
19     v.push_back(std::make_unique<Book>("Harry Potter", "J.K. Rowling"));
20     v.push_back(std::make_unique<Film>("The Godfather", "Francis Ford Coppola"));
21     v.push_back(std::make_unique<Music>("Blowing in the wind", "Bob Dylan"));
22     for (const auto& ptr : v) {
23         ptr->publish();
24         ptr->use();
25         std::cout << '\n';
26     }
27 }
28 void test3() {
29     Book book("A Philosophy of Software Design", "John Ousterhout");
30     book.publish();
31     book.use();
32 }
33 int main() {
34     std::cout << "运行时多态:纯虚函数、抽象类\n";
35     std::cout << "\n测试1: 使用原始指针\n";
36     test1();
37     std::cout << "\n测试2: 使用智能指针\n";
38     test2();
39     std::cout << "\n测试3: 直接使用类\n";
40     test3();
41 }
task1.cpp

 

 

运行结果截图:

 

image

 回答问题:

问题1:抽象类机制

(1)是什么决定了 Publisher 是抽象类?用一句话说明,并指出代码中的具体依据。

  含有纯虚函数。

  代码中的依据:virtual void publish() const = 0

         virtual void use() const = 0

  声明了两个纯虚函数,因此是抽象类。

 

(2)如果在 main.cpp 里直接写 Publisher p; 能否编译通过?为什么?

  不能编译通过。因为 Publisher 是抽象类,抽象类无法实例化对象。

 

问题2:纯虚函数与接口继承

(1) Book 、 Film 、 Music 必须实现哪两个函数才能通过编译?请写出其完整函数声明。

  void publish() const override;
  void use() const override;

 

(2) 在 publisher.cpp 的 Film 类实现中,把两个成员函数实现里的 const 去掉(保持函数体不变),重新 编译,报错信息是什么?

   对非静态成员“Publisher::name”的非法引用。

 

问题3:运行时多态与虚析构

(1)在 test1() 里,for (Publisher *ptr : v) 中 ptr 的声明类型是什么?

  Publisher 类的指针:Publisher*

(2)当循环执行到 ptr->publish(); 时,ptr 实际指向的对象类型分别有哪些?(按循环顺序写出)

  Book、Film、Music。

(3)基类 Publisher 的析构函数为何声明为 virtual?若删除 virtual,执行 delete ptr; 会出现什么 问题?

   基类Publisher的析构函数声明为 virtual 是为了实现多态析构,确保能够正确删除派生类对象,避免内存泄漏。若删除 virtual,执行 delete ptr 时只会调用基类的析构函数,派生类的成员不会被删除,导致内存泄漏。

 

实验任务二:

源代码:

 1 #pragma once
 2 #include <string>
 3 
 4 // 图书描述信息类Book: 声明
 5 class Book {
 6 public:
 7     Book(const std::string& name_,
 8         const std::string& author_,
 9         const std::string& translator_,
10         const std::string& isbn_,
11         double price_);
12     friend std::ostream& operator<<(std::ostream& out, const Book& book);
13 
14 private:
15     std::string name;        // 书名
16     std::string author;      // 作者
17     std::string translator;  // 译者
18     std::string isbn;        // isbn号
19     double price;            // 定价
20 };
book.hpp
 1 #pragma once
 2 
 3 #include <string>
 4 #include "book.hpp"
 5 
 6 // 图书销售记录类BookSale:声明
 7 class BookSale {
 8 public:
 9     BookSale(const Book& rb_, double sales_price_, int sales_amount_);
10     int get_amount() const;         // 返回销售数量
11     double get_revenue() const;     // 返回营收
12 
13     friend std::ostream& operator<<(std::ostream& out, const BookSale& item);
14 
15 private:
16     Book rb;                // 图书信息
17     double sales_price;     // 售价
18     int sales_amount;       // 销售数量
19 };
booksale.hpp
 1 #include <iomanip>
 2 #include <iostream>
 3 #include <string>
 4 #include "book.hpp"
 5 
 6 // 图书描述信息类Book: 实现
 7 Book::Book(const std::string& name_,
 8     const std::string& author_,
 9     const std::string& translator_,
10     const std::string& isbn_,
11     double price_) : name{ name_ }, author{ author_ }, translator{ translator_ }, isbn{ isbn_ }, price{ price_ } {
12 }
13 
14 // 运算符<<重载实现
15 std::ostream& operator<<(std::ostream& out, const Book& book) {
16     using std::left;
17     using std::setw;
18 
19     out << left;
20     out << setw(15) << "书名:" << book.name << '\n'
21         << setw(15) << "作者:" << book.author << '\n'
22         << setw(15) << "译者:" << book.translator << '\n'
23         << setw(15) << "ISBN:" << book.isbn << '\n'
24         << setw(15) << "定价:" << book.price;
25 
26     return out;
27 }
book.cpp
#include <iomanip>
#include <iostream>
#include <string>
#include "booksale.hpp"

// 图书销售记录类BookSale:实现
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;
}
booksale.cpp
 1 #include "booksale.hpp"
 2 #include <iostream>
 3 #include <string>
 4 #include <vector>
 5 #include <algorithm>
 6 
 7 // 按图书销售数额比较
 8 bool compare_by_amount(const BookSale& x1, const BookSale& x2) {
 9     return x1.get_amount() > x2.get_amount();
10 }
11 
12 void test() {
13     using namespace std;
14 
15     vector<BookSale> sales_lst; // 存放图书销售记录
16 
17     int books_number;
18     cout << "录入图书数量: ";
19     cin >> books_number;
20 
21     cout << "录入图书销售记录" << endl;
22     for (int i = 0; i < books_number; ++i) {
23         string name, author, translator, isbn;
24         float price;
25         cout << string(20, '-') << "" << i + 1 << "本图书信息录入" << string(20, '-') << endl;
26         cout << "录入书名: "; cin >> name;
27         cout << "录入作者: "; cin >> author;
28         cout << "录入译者: "; cin >> translator;
29         cout << "录入isbn: "; cin >> isbn;
30         cout << "录入定价: "; cin >> price;
31 
32         Book book(name, author, translator, isbn, price);
33 
34         float sales_price;
35         int sales_amount;
36 
37         cout << "录入售价: "; cin >> sales_price;
38         cout << "录入销售数量: "; cin >> sales_amount;
39 
40         BookSale record(book, sales_price, sales_amount);
41         sales_lst.push_back(record);
42     }
43 
44     // 按销售册数排序
45     sort(sales_lst.begin(), sales_lst.end(), compare_by_amount);
46 
47     // 按销售册数降序输出图书销售信息
48     cout << string(20, '=') << "图书销售统计" << string(20, '=') << endl;
49     for (auto& t : sales_lst) {
50         cout << t << endl;
51         cout << string(40, '-') << endl;
52     }
53 }
54 
55 int main() {
56     test();
57 }
task2.cpp

运行结果截图:

image

回答问题:

问题1:重载运算符<<
(1)找出运算符<<被重载了几处?分别用于什么类型?

  两处;Book和Booksale类型。
(2)找出使用重载<<输出对象的代码,写在下面。

 book.cpp中
std::ostream& operator<<(std::ostream &out, const Book &book);
booksale.cpp中 std::ostream& operator<<(std::ostream &out, const BookSale &item); task2.cpp中调用 cout << t << endl;


问题2:图书销售统计
(1)图书销售记录"按销售数量降序排序",代码是如何实现的?

  sort(sales_lst.begin(), sales_lst.end(), compare_by_amount);

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

  使用标准库函数sort和自定义函数compare_by_amount实现按销售数量降序排序。
(2)拓展(选答*):如果使用lambda表达式,如何实现"按销售数量降序排序"?

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

实验任务三:

源代码:

#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();
}
task3_1.cpp
 1 #include <iostream>
 2 #include <string>
 3 // 定义类模板
 4 template<typename T>
 5 class X {
 6 public:
 7     X(T x0, T y0);
 8     void display();
 9 private:
10     T x, y;
11 };
12 template<typename T>
13 X<T>::X(T x0, T y0) : x{ x0 }, y{ y0 } {
14 }
15 template<typename T>
16 void X<T>::display() {
17     std::cout << x << ", " << y << '\n';
18 }
19 void test() {
20     std::cout << "测试1: 用int实例化类模板X" << '\n';
21     X<int> x1(3, 4);
22     x1.display();
23     std::cout << "\n测试2: 用double实例化类模板X" << '\n';
24     X<double> x2(3.2, 5.6);
25     x2.display();
26     std::cout << "\n测试3: 用string实例化类模板X" << '\n';
27     X<std::string> x3("hello", "oop");
28     x3.display();
29 }
30 
31 int main() {
32     test();
33 }
task3_2.cpp

运行结果截图:

image 

image

 实验任务四:

源代码:

 1 #pragma once
 2 
 3 #include<string>
 4 using namespace std;
 5 
 6 class MachinePet {
 7 private:
 8     string nickname;
 9 public:
10     MachinePet(const string name):nickname{name} {}
11     virtual ~MachinePet() = default;
12 
13     string get_nickname() const{
14         return nickname;
15     }
16     virtual string talk() const = 0;
17 };
18 class PetCat : public MachinePet {
19 public:
20    
21     PetCat(const string& nickname_) : MachinePet( nickname_ ) {}
22 
23    
24     string talk() const override {
25         return "miao wu~";
26     }
27 };
28 
29 class PetDog : public MachinePet {
30 public:
31    
32     PetDog(const string& nickname_) : MachinePet(nickname_ ) {}
33 
34     
35     string talk() const override {
36         return "wang wang~";
37     }
38 };
pet.hpp
 1 #include <iostream>
 2 #include <memory>
 3 #include <vector>
 4 #include "pet.hpp"
 5 void test1() {
 6     std::vector<MachinePet*> pets;
 7     pets.push_back(new PetCat("miku"));
 8     pets.push_back(new PetDog("da huang"));
 9     
10         for (MachinePet* ptr : pets) {
11             std::cout << ptr->get_nickname() << " says " << ptr->talk() << '\n';
12             delete ptr;  // 须手动释放资源
13         }
14 }
15 void test2() {
16     std::vector<std::unique_ptr<MachinePet>> pets;
17     pets.push_back(std::make_unique<PetCat>("miku"));
18     pets.push_back(std::make_unique<PetDog>("da huang"));
19     for (auto const& ptr : pets)
20     std::cout << ptr->get_nickname() << " says " << ptr->talk() << '\n';
21 }
22     
23 void test3() {
24     // MachinePet pet("little cutie");   // 编译报错:无法定义抽象类对象
25     const PetCat cat("miku");
26     std::cout << cat.get_nickname() << " says " << cat.talk() << '\n';
27     const PetDog dog("da huang");
28     std::cout << dog.get_nickname() << " says " << dog.talk() << '\n';
29 }
30 int main() {
31     std::cout << "测试1: 使用原始指针\n";
32     test1();
33     std::cout << "\n测试2: 使用智能指针\n";
34     test2();
35     std::cout << "\n测试3: 直接使用类\n";
36     test3();
37 }
task4.cpp

 

运行结果截图:

image

 

实验任务五:

源代码:

 

#pragma once

#include<iostream>
using namespace std;

template<typename T>

class Complex {
private:
    T real, imag;
public:
    Complex(T a=0,T b=0):real{a},imag{b} {}
    Complex(const Complex& c) {
        real = c.real;
        imag = c.imag;
    }
    ~Complex() = default;

    T get_real() const {
        return real;
    }
    T get_imag() const {
        return imag;
    }
    void operator+=(const Complex& other) {
        this->real += other.real;
        this->imag += other.imag;
    }

    friend Complex operator+(const Complex& c1, const Complex& c2) {
        Complex temp;
        temp.real = c1.real + c2.real;
        temp.imag = c1.imag + c2.imag;
        return temp;
    }

    friend bool operator==(const Complex& c1, const Complex& c2) {
        return (c1.real == c2.real) && (c1.imag == c2.imag);
    }

    
    friend ostream& operator<<(ostream& out, const Complex& c) {
        
        if (c.imag >= 0) {
            out <<c.real<< "+" << c.imag << "i";
        }
        else {
            out <<c.real<< c.imag << "i";
        }
        return out;
    }

     friend istream& operator>>(istream& in, Complex& c) {
        in >> c.real >> c.imag;
        return in;
    }

};
Complex.hpp
#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();
}
task3.cpp

 

运行结果截图:

 

image

 

实验总结:

 

1.深入理解了抽象类与纯虚函数的机制:抽象类通过纯虚函数定义接口,强制派生类实现具体功能,有效规范了类的继承体系,同时抽象类无法实例化的特性避免了不合理的对象创建。

2.复习了运算符重载相关代码。

3.重温了类模板的定义与使用,类模板将类型参数化,有效减少了代码量。

 

posted @ 2025-12-14 16:31  pithia  阅读(3)  评论(0)    收藏  举报