OOP-实验5

实验任务1

源代码task1

 1 // 类Publisher及其派生类Book, Film, Music声明
 2 #pragma once
 3 
 4 #include <string>
 5 
 6 // 发行/出版物类:Publisher(抽象类)
 7 class Publisher
 8 {
 9 public:
10     Publisher(const std::string &name_ = ""); // 构造函数
11     virtual ~Publisher() = default;
12 
13 public:
14     virtual void publish() const = 0; // 纯虚函数,作为接口继承
15     virtual void use() const = 0;     // 纯虚函数,作为接口继承
16 
17 protected:
18     std::string name; // 发行/出版物名称
19 };
20 
21 // 图书类:Book
22 class Book : public Publisher
23 {
24 public:
25     Book(const std::string &name_ = "", const std::string &author_ = ""); // 构造函数
26 
27 public:
28     void publish() const override; // 接口
29     void use() const override;     // 接口
30 
31 private:
32     std::string author; // 作者
33 };
34 
35 // 电影类:Film
36 class Film : public Publisher
37 {
38 public:
39     Film(const std::string &name_ = "", const std::string &director_ = ""); // 构造函数
40 
41 public:
42     void publish() const override; // 接口
43     void use() const override;     // 接口
44 
45 private:
46     std::string director; // 导演
47 };
48 
49 // 音乐类:Music
50 class Music : public Publisher
51 {
52 public:
53     Music(const std::string &name_ = "", const std::string &artist_ = ""); // 构造函数
54 
55 public:
56     void publish() const override; // 接口
57     void use() const override;     // 接口
58 
59 private:
60     std::string artist; // 音乐艺术家名称
61 };
Publisher.hpp
 1 // 类Publisher及其派生类Book, Film, Music实现
 2 
 3 #include <iostream>
 4 #include <string>
 5 
 6 #include "publisher.hpp"
 7 
 8 // Publisher类:实现
 9 Publisher::Publisher(const std::string &name_) : name{name_} {}
10 
11 // Book类:实现
12 Book::Book(const std::string &name_, const std::string &author_) : Publisher{name_}, author{author_} {}
13 
14 void Book::publish() const
15 {
16     std::cout << "Publishing book 《" << name << "》 by " << author << std::endl;
17 }
18 
19 void Book::use() const
20 {
21     std::cout << "Reading book 《" << name << "》 by " << author << std::endl;
22 }
23 
24 // Film类:实现
25 Film::Film(const std::string &name_, const std::string &director_) : Publisher{name_}, director{director_} {}
26 
27 void Film::publish() const
28 {
29     std::cout << "Publishing film <" << name << "> directed by " << director << std::endl;
30 }
31 
32 void Film::use() const
33 {
34     std::cout << "Watching film <" << name << "> directed by " << director << std::endl;
35 }
36 
37 // Music类:实现
38 Music::Music(const std::string &name_, const std::string &artist_) : Publisher{name_}, artist{artist_} {}
39 
40 void Music::publish() const
41 {
42     std::cout << "Publishing music <" << name << "> by " << artist << std::endl;
43 }
44 
45 void Music::use() const
46 {
47     std::cout << "Listening to music <" << name << "> by " << artist << std::endl;
48 }
Publisher.cpp
 1 // 综合应用组合、继承、多态实现出版物的分类管理
 2 
 3 #include <memory>
 4 #include <iostream>
 5 #include <vector>
 6 
 7 #include "publisher.hpp"
 8 #include "publisher.cpp"
 9 
10 void test1()
11 {
12     std::vector<Publisher *> v;
13 
14     v.push_back(new Book("Harry Potter", "J.K. Rowling"));
15     v.push_back(new Film("The Godfather", "Francis Ford Coppola"));
16     v.push_back(new Music("Blowing in the wind", "Bob Dylan"));
17 
18     for (Publisher *ptr : v)
19     {
20         ptr->publish();
21         ptr->use();
22 
23         std::cout << std::endl;
24 
25         delete ptr;
26     }
27 }
28 
29 void test2()
30 {
31     std::vector<std::unique_ptr<Publisher>> v;
32 
33     v.push_back(std::make_unique<Book>("Harry Potter", "J.K. Rowling"));
34     v.push_back(std::make_unique<Film>("The Godfather", "Francis Ford Coppola"));
35     v.push_back(std::make_unique<Music>("Blowing in the wind", "Bob Dylan"));
36 
37     for (const auto &ptr : v)
38     {
39         ptr->publish();
40         ptr->use();
41 
42         std::cout << std::endl;
43     }
44 }
45 
46 void test3()
47 {
48     Book book("A Philosophy of Software Design", "John Ousterhout");
49 
50     book.publish();
51     book.use();
52 }
53 
54 int main()
55 {
56     std::cout << "运行时多态:纯虚函数、抽象类" << std::endl;
57 
58     std::cout << std::endl;
59 
60     std::cout << "测试1:使用原始指针" << std::endl;
61     test1();
62 
63     std::cout << std::endl;
64 
65     std::cout << "测试2:使用智能指针" << std::endl;
66     test2();
67 
68     std::cout << std::endl;
69 
70     std::cout << "测试3:直接使用类" << std::endl;
71     test3();
72 }
task1.cpp

 运行测试截图

task1

回答问题

问题1:抽象类机制

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

抽象类:含有纯虚函数的类

抽象类 Publisher 含有两个纯虚函数 publish() 和 use()

virtual void publish() const = 0;
virtual void use() const = 0;

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

不能;

原因:抽象类 Publisher 不能定义对象来实例化,只能定义指针或引用

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

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

必须实现抽象基类 Publisher 中的两个纯虚函数 publish() 和 use()

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

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

task1_error

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

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

声明类型:Publisher*,指向抽象基类 Publisher 的指针

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

Book*,即指向 Book 类对象;

Film*,即指向 Film 类对象;

Music*,即指向 Music 类对象

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

有虚函数时,析构函数必须加 virrtual;

若析构函数不定义为虚函数:只会调用基类的析构函数,释放基类的空间;而派生类的析构函数则不会被调用,导致空间泄露

 

实验任务2

源代码task2

 1 // 图书描述信息类Book声明
 2 
 3 #pragma once
 4 
 5 #include <string>
 6 
 7 // 图书描述信息类Book:声明
 8 class Book
 9 {
10 public:
11     Book(const std::string &name_,
12          const std::string &author_,
13          const std::string &translator_,
14          const std::string &isbn_,
15          double price_);
16 
17     friend std::ostream &operator<<(std::ostream &out, const Book &book);
18 
19 private:
20     std::string name;       // 书名
21     std::string author;     // 作者
22     std::string translator; // 译者
23     std::string isbn;       // isbn号
24 
25     double price; // 定价
26 };
book.hpp
 1 // 图书描述信息类Book实现
 2 
 3 #include <iomanip>
 4 #include <iostream>
 5 #include <string>
 6 
 7 #include "book.hpp"
 8 
 9 // 图书描述信息类Book:实现
10 Book::Book(const std::string &name_,
11            const std::string &author_,
12            const std::string &translator_,
13            const std::string &isbn_,
14            double price_) : name{name_}, author{author_}, translator{translator_}, isbn{isbn_}, price{price_}
15 {
16 }
17 
18 // 运算符<<重载实现
19 std::ostream &operator<<(std::ostream &out, const Book &book)
20 {
21     using std::left;
22     using std::setw;
23 
24     out << left;
25     out << setw(15) << "书名:" << book.name << '\n'
26         << setw(15) << "作者:" << book.author << '\n'
27         << setw(15) << "译者:" << book.translator << '\n'
28         << setw(15) << "ISBN:" << book.isbn << '\n'
29         << setw(15) << "定价:" << book.price;
30 
31     return out;
32 }
book.cpp
 1 // 图书销售记录类BookSale声明
 2 
 3 #pragma once
 4 
 5 #include <string>
 6 
 7 #include "book.hpp"
 8 
 9 // 图书销售记录类BookSale:声明
10 class BookSale
11 {
12 public:
13     BookSale(const Book &rb_, double sales_price_, int sales_amount_);
14     int get_amount() const;     // 返回销售数量
15     double get_revenue() const; // 返回营收
16 
17     friend std::ostream &operator<<(std::ostream &out, const BookSale &item);
18 
19 private:
20     Book rb;
21     double sales_price; // 售价
22     int sales_amount;   // 销售数量
23 };
booksale.hpp
 1 // 图书销售记录类BookSale实现
 2 
 3 #include <iomanip>
 4 #include <iostream>
 5 #include <string>
 6 
 7 #include "booksale.hpp"
 8 
 9 // 图书销售记录类BookSale:实现
10 BookSale::BookSale(const Book &rb_,
11                    double sales_price_,
12                    int sales_amount_) : rb{rb_}, sales_price{sales_price_}, sales_amount{sales_amount_}
13 {
14 }
15 
16 int BookSale::get_amount() const
17 {
18     return sales_amount;
19 }
20 
21 double BookSale::get_revenue() const
22 {
23     return sales_price * sales_amount;
24 }
25 
26 // 运算符<<重载实现
27 std::ostream &operator<<(std::ostream &out, const BookSale &item)
28 {
29     using std::left;
30     using std::setw;
31 
32     out << left;
33     out << item.rb << '\n'
34         << setw(15) << "售价:" << item.sales_price << '\n'
35         << setw(15) << "销售数量:" << item.sales_amount << '\n'
36         << setw(15) << "营收:" << item.get_revenue();
37 
38     return out;
39 }
booksale.cpp
 1 // 综合应用运算符重载、组合、标准库实现图书销售统计
 2 
 3 #include <iostream>
 4 #include <string>
 5 #include <vector>
 6 #include <algorithm>
 7 
 8 #include "book.hpp"
 9 #include "book.cpp"
10 #include "booksale.hpp"
11 #include "booksale.cpp"
12 
13 // 按图书销售数额比较
14 bool compare_by_amount(const BookSale &x1, const BookSale &x2)
15 {
16     return x1.get_amount() > x2.get_amount();
17 }
18 
19 void test()
20 {
21     using namespace std;
22 
23     vector<BookSale> sales_list; // 存放图书销售记录
24 
25     int books_number;
26     cout << "录入图书数量: ";
27     cin >> books_number;
28 
29     cout << "录入图书销售记录" << endl;
30     for (int i = 1; i <= books_number; i++)
31     {
32         string name, author, translator, isbn;
33         float price;
34 
35         cout << string(20, '-') << "" << i << "本图书信息录入" << string(20, '-') << endl;
36 
37         cout << "录入书名: ";
38         cin >> name;
39 
40         cout << "录入作者: ";
41         cin >> author;
42 
43         cout << "录入译者: ";
44         cin >> translator;
45 
46         cout << "录入isbn: ";
47         cin >> isbn;
48 
49         cout << "录入定价: ";
50         cin >> price;
51 
52         Book book(name, author, translator, isbn, price);
53 
54         float sales_price;
55         float sales_amount;
56 
57         cout << "录入售价: ";
58         cin >> sales_price;
59 
60         cout << "录入销售数量: ";
61         cin >> sales_amount;
62 
63         BookSale record(book, sales_price, sales_amount);
64         sales_list.push_back(record);
65     }
66 
67     // 按销售册数排序
68     sort(sales_list.begin(), sales_list.end(), compare_by_amount);
69 
70     // 按销售册数降序输出图书销售信息
71     cout << string(20, '=') << "图书销售统计" << string(20, '=') << endl;
72     for (auto &t : sales_list)
73     {
74         cout << t << endl;
75         cout << string(40, '-') << endl;
76     }
77 }
78 
79 int main()
80 {
81     test();
82 }
task2.cpp

运行测试截图

task2

回答问题

问题1:重载运算符 <<

(1)找出 << 被重载了几处?用于哪些类型?

2处;

用于 Book 类输出:std::ostream &operator<<(std::ostream &out, const Book &book);

用于BookSale 类输出:std::ostream &operator<<(std::ostream &out, const BookSale &item);

(2)找出使用重载 << 输出对象的代码,写在下面。

out << item.rb
cout << t << endl;

问题2:图书销售统计

(1)“按销售数量降序排序”,描述降序排序实现方式。

使用 std::sort() 函数、采取 compare_by_amount() 逻辑实现降序排序;

自定义函数 compare_by_amount() -> 销售数量更大的在前,从而实现降序排序

(2)拓展(选答*):如果使用 lambda 表达式,如何实现?

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

实验任务3

源代码task3

 1 // 类模板定义和使用
 2 // 类A定义 + 类B定义 + 测试模块 + main
 3 
 4 #include <iostream>
 5 
 6 // 类A的定义
 7 class A
 8 {
 9 public:
10     A(int x0, int y0);
11     void display() const;
12 
13 private:
14     int x, y;
15 };
16 
17 A::A(int x0, int y0) : x{x0}, y{y0} {}
18 
19 void A::display() const
20 {
21     std::cout << x << ", " << y << std::endl;
22 }
23 
24 // 类B的定义
25 class B
26 {
27 public:
28     B(double x0, double y0);
29     void display() const;
30 
31 private:
32     double x, y;
33 };
34 
35 B::B(double x0, double y0) : x{x0}, y{y0} {}
36 
37 void B::display() const
38 {
39     std::cout << x << ", " << y << std::endl;
40 }
41 
42 void test()
43 {
44     std::cout << "测试类A: " << std::endl;
45     A a(3, 4);
46     a.display();
47 
48     std::cout << std::endl;
49 
50     std::cout << "测试类B: " << std::endl;
51     B b(3.2, 5.6);
52     b.display();
53 }
54 
55 int main()
56 {
57     test();
58 }
task3_1.cpp
 1 // 类模板定义和使用
 2 // 类模板X定义 + 测试模块 + main
 3 
 4 #include <iostream>
 5 #include <string>
 6 
 7 // 定义类模板
 8 template <typename T>
 9 class X
10 {
11 public:
12     X(T x0, T y0);
13     void display() const;
14 
15 private:
16     T x, y;
17 };
18 
19 template <typename T>
20 X<T>::X(T x0, T y0) : x{x0}, y{y0} {}
21 
22 template <typename T>
23 void X<T>::display() const
24 {
25     std::cout << x << ", " << y << std::endl;
26 }
27 
28 void test()
29 {
30     std::cout << "测试1: 用int实例化类模板X" << std::endl;
31     X<int> x1(3, 4);
32     x1.display();
33 
34     std::cout << std::endl;
35 
36     std::cout << "测试2: 用double实例化类模板X" << std::endl;
37     X<double> x2(3.2, 5.6);
38     x2.display();
39 
40     std::cout << std::endl;
41 
42     std::cout << "测试3: 用string实例化类模板X" << std::endl;
43     X<std::string> x3("hello", "oop");
44     x3.display();
45 }
46 
47 int main()
48 {
49     test();
50 }
task3_2.cpp

运行测试截图

task3_1

task3_2

 

实验任务4

源代码task4

 1 #pragma once
 2 
 3 #include <iostream>
 4 #include <string>
 5 
 6 class MachinePet
 7 {
 8 public:
 9     MachinePet(const std::string &nickname_);
10     std::string get_nickname() const;
11 
12     virtual ~MachinePet() = default;
13 
14     virtual std::string talk() const = 0;
15 
16 protected:
17     std::string nickname;
18 };
19 
20 MachinePet::MachinePet(const std::string &nickname_) : nickname{nickname_} {}
21 
22 std::string MachinePet::get_nickname() const
23 {
24     return nickname;
25 }
26 
27 class PetCat : public MachinePet
28 {
29 public:
30     PetCat(const std::string &nickname_);
31     std::string talk() const override;
32 };
33 
34 PetCat::PetCat(const std::string &nickname_) : MachinePet{nickname_} {}
35 
36 std::string PetCat::talk() const
37 {
38     return "miao wu~";
39 }
40 
41 class PetDog : public MachinePet
42 {
43 public:
44     PetDog(const std::string &nickname_);
45     std::string talk() const override;
46 };
47 
48 PetDog::PetDog(const std::string &nickname_) : MachinePet{nickname_} {}
49 
50 std::string PetDog::talk() const
51 {
52     return "wang wang~";
53 }
pet.hpp
 1 // 综合应用继承、多态,模拟简单机器宠物
 2 
 3 #include <memory>
 4 #include <iostream>
 5 #include <vector>
 6 
 7 #include "pet.hpp"
 8 
 9 void test1()
10 {
11     std::vector<MachinePet *> pets;
12 
13     pets.push_back(new PetCat("miku"));
14     pets.push_back(new PetDog("da huang"));
15 
16     for (MachinePet *ptr : pets)
17     {
18         std::cout << ptr->get_nickname() << " says " << ptr->talk() << std::endl;
19         delete ptr; // 须手动释放资源
20     }
21 }
22 
23 void test2()
24 {
25     std::vector<std::unique_ptr<MachinePet>> pets;
26 
27     pets.push_back(std::make_unique<PetCat>("miku"));
28     pets.push_back(std::make_unique<PetDog>("da huang"));
29 
30     for (auto const &ptr : pets)
31         std::cout << ptr->get_nickname() << " says " << ptr->talk() << '\n';
32 }
33 
34 void test3()
35 {
36     // MachinePet pet("little cutie"); // 编译报错:无法定义抽象类对象
37 
38     const PetCat cat("miku");
39     std::cout << cat.get_nickname() << " says " << cat.talk() << '\n';
40 
41     const PetDog dog("da huang");
42     std::cout << dog.get_nickname() << " says " << dog.talk() << '\n';
43 }
44 
45 int main()
46 {
47     std::cout << "测试1: 使用原始指针" << std::endl;
48     test1();
49 
50     std::cout << std::endl;
51 
52     std::cout << "测试2: 使用智能指针" << std::endl;
53     test2();
54 
55     std::cout << std::endl;
56 
57     std::cout << "测试3: 直接使用类" << std::endl;
58     test3();
59 }
task4.cpp

运行测试截图

task4

 

实验任务5

源代码task5

  1 #pragma once
  2 
  3 #include <iostream>
  4 
  5 // 报错:链接器错误:链接时找不到模板函数的定义
  6 // 模板函数必须在头文件中定义,不可只在头文件中声明而在单独的.cpp文件中实现
  7 // 若坚持分开:需要在cpp文件中显式实例化模板
  8 
  9 // 模板友元函数声明不匹配:
 10 // 方法1:直接在类内部定义友元函数
 11 // 方法2:前向声明(在类外部实现)
 12 
 13 // 前向声明
 14 template <typename T>
 15 class Complex;
 16 
 17 // 前向声明模板友元函数
 18 template <typename T>
 19 Complex<T> operator+(const Complex<T> &c1, const Complex<T> &c2);
 20 
 21 template <typename T>
 22 bool operator==(const Complex<T> &c1, const Complex<T> &c2);
 23 
 24 template <typename T>
 25 std::istream &operator>>(std::istream &in, Complex<T> &c);
 26 
 27 template <typename T>
 28 std::ostream &operator<<(std::ostream &out, const Complex<T> &c);
 29 
 30 template <typename T>
 31 class Complex
 32 {
 33 public:
 34     Complex(T real_ = 0, T imag_ = 0);
 35     Complex(const Complex &c);
 36 
 37     T get_real() const;
 38     T get_imag() const;
 39 
 40     Complex &operator+=(const Complex &c);
 41 
 42     friend Complex<T> operator+ <T>(const Complex<T> &c1, const Complex<T> &c2);
 43     friend bool operator== <T>(const Complex<T> &c1, const Complex<T> &c2);
 44     friend std::istream &operator>> <T>(std::istream &in, Complex<T> &c);
 45     friend std::ostream &operator<< <T>(std::ostream &out, const Complex<T> &c);
 46 
 47 private:
 48     T real, imag;
 49 };
 50 
 51 template <typename T>
 52 Complex<T>::Complex(T real_, T imag_) : real{real_}, imag{imag_} {}
 53 
 54 template <typename T>
 55 Complex<T>::Complex(const Complex &c)
 56 {
 57     real = c.get_real();
 58     imag = c.get_imag();
 59 }
 60 
 61 template <typename T>
 62 T Complex<T>::get_real() const
 63 {
 64     return real;
 65 }
 66 
 67 template <typename T>
 68 T Complex<T>::get_imag() const
 69 {
 70     return imag;
 71 }
 72 
 73 template <typename T>
 74 Complex<T> &Complex<T>::operator+=(const Complex &c)
 75 {
 76     real += c.get_real();
 77     imag += c.get_imag();
 78 
 79     return *this;
 80 }
 81 
 82 template <typename T>
 83 Complex<T> operator+(const Complex<T> &c1, const Complex<T> &c2)
 84 {
 85     T real = c1.get_real() + c2.get_real();
 86     T imag = c1.get_imag() + c2.get_imag();
 87 
 88     Complex<T> c(real, imag);
 89 
 90     return c;
 91 }
 92 
 93 template <typename T>
 94 bool operator==(const Complex<T> &c1, const Complex<T> &c2)
 95 {
 96     if (c1.get_real() == c2.get_real() && c1.get_imag() == c2.get_imag())
 97         return true;
 98 
 99     return false;
100 }
101 
102 template <typename T>
103 std::istream &operator>>(std::istream &in, Complex<T> &c)
104 {
105     in >> c.real >> c.imag;
106     return in;
107 }
108 
109 template <typename T>
110 std::ostream &operator<<(std::ostream &out, const Complex<T> &c)
111 {
112     if (c.get_imag() > 0)
113         out << c.get_real() << " + " << c.get_imag() << 'i';
114 
115     else
116         out << c.get_real() << " - " << -c.get_imag() << 'i';
117 
118     return out;
119 }
Complex.hpp
 1 // 综合应用运算符重载、类模板实现编译时多态
 2 
 3 #include <iostream>
 4 
 5 #include "Complex.hpp"
 6 
 7 void test1()
 8 {
 9     using std::boolalpha;
10     using std::cout;
11 
12     Complex<int> c1(2, -5), c2(c1);
13 
14     cout << "c1 = " << c1 << '\n';
15     cout << "c2 = " << c2 << '\n';
16     cout << "c1 + c2 = " << c1 + c2 << '\n';
17 
18     c1 += c2;
19     cout << "c1 = " << c1 << '\n';
20     cout << boolalpha << (c1 == c2) << '\n';
21 }
22 
23 void test2()
24 {
25     using std::cin;
26     using std::cout;
27 
28     Complex<double> c1, c2;
29     cout << "Enter c1 and c2: ";
30     cin >> c1 >> c2;
31     cout << "c1 = " << c1 << '\n';
32     cout << "c2 = " << c2 << '\n';
33 
34     const Complex<double> c3(c1);
35     cout << "c3.real = " << c3.get_real() << '\n';
36     cout << "c3.imag = " << c3.get_imag() << '\n';
37 }
38 
39 int main()
40 {
41     std::cout << "自定义类模板Complex测试1: \n";
42     test1();
43 
44     std::cout << std::endl;
45 
46     std::cout << "自定义类模板Complex测试2: \n";
47     test2();
48 }
task5.cpp

运行测试截图

task5

 

实验总结

1. 类模板:在类外实现成员函数时,必须加上模板头并写出完整类名 X<T>

2. 模板函数必须在头文件中定义,不可只在头文件中声明而在单独的.cpp文件中实现;

3. 对于模板友元函数声明不匹配:

(1)直接在类内部完成对友元函数的定义;

(2)前向声明(在类外部具体实现)

posted @ 2025-12-10 19:57  FF-10086  阅读(6)  评论(0)    收藏  举报