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 };
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 }
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 }
运行测试截图

回答问题
问题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 去掉(保持函数体不变),重新编译,报错信息是什么?

问题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 };
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 }
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 };
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 }
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 }
运行测试截图

回答问题
问题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 }
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 }
运行测试截图


实验任务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 }
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 }
运行测试截图

实验任务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 }
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 }
运行测试截图

实验总结
1. 类模板:在类外实现成员函数时,必须加上模板头并写出完整类名 X<T>
2. 模板函数必须在头文件中定义,不可只在头文件中声明而在单独的.cpp文件中实现;
3. 对于模板友元函数声明不匹配:
(1)直接在类内部完成对友元函数的定义;
(2)前向声明(在类外部具体实现)

浙公网安备 33010602011771号