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

问题1:抽象类机制
(1)是什么决定了 Publisher 是抽象类?用一句话说明,并指出代码中的具体依据。
答:它包含纯虚函数,依据是类中声明:virtual void publish() const = 0; virtual void use() const = 0;
(2)如果在 main.cpp 里直接写 Publisher p; 能否编译通过?为什么?
答:不能,因为抽象类不能实例化
问题2:纯虚函数与接口继承
(1)Book 、Film 、Music 必须实现哪两个函数才能通过编译?请写出其完整函数声明。
答:这三个类中都必须实现void publish() const override;void use() const override;
(2) 在 publisher.cpp 的 Film 类实现中,把两个成员函数实现里的 const 去掉(保持函数体不变),重新编译,报错信息是什么?
答:函数与基类中的不匹配
问题3:运行时多态与虚析构
(1)在 test1() 里,for (Publisher *ptr : v) 中ptr 的声明类型是什么?
答:Publisher 类型的指针
(2)当循环执行到 ptr->publish(); 时,ptr 实际指向的对象类型分别有哪些?(按循环顺序写出)
答:Book,Film,Music
(3)基类 Publisher 的析构函数为何声明为virtual ?若删除 virtual ,执行 delete ptr; 会出 现什么问题
答:ptr可以调用派生类析构函数,若删除,无法调用派生类析构函数,部分派生类资源不被释放
任务二
book.hpp
1 #pragma once 2 #include<string> 3 class Book 4 { 5 public: 6 Book(const std::string &name_,const std::string &author_,const std::string &translator_,const std::string &isbn_,double price_); 7 friend std::ostream& operator<<(std::ostream &out,const Book &book); 8 private: 9 std::string name; 10 std::string author; 11 std::string translator; 12 std::string isbn; 13 double price; 14 };
book.cpp
1 #include<iostream> 2 #include<string> 3 #include"book.hpp" 4 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_} 5 { 6 } 7 std::ostream& operator<<(std::ostream &out,const Book &book) 8 { 9 using std::left; 10 using std::setw; 11 out<<left; 12 out<<setw(15)<<"书名:"<<book.name<<'\n'<<setw(15)<<"作者:"<<book.author<<'\n'<<setw(15) << "译者:" << book.translator << '\n' 13 << setw(15) << "ISBN:" << book.isbn << '\n'<< setw(15) << "定价:" << book.price; 14 return out; 15 }
booksale.hpp
1 #pragma once 2 #include<string> 3 #include"book.hpp" 4 class BookSale 5 { 6 public: 7 BookSale(const Book &rb_,double sales_price_,int sales_amount_); 8 int get_amount() const; 9 double get_revenue() const; 10 friend std::ostream& operator<<(std::ostream &out,const BookSale &item); 11 private: 12 Book rb; 13 double sales_price; 14 int sales_amount; 15 };
booksale.cpp
1 #include<iomanip> 2 #include<iostream> 3 #include<string> 4 #include"booksale.hpp" 5 BookSale::BookSale(const Book &rb_,double sales_price_,int sales_amount_):rb{rb_},sales_price{sales_price_},sales_amount{sales_amount_} 6 { 7 } 8 int BookSale::get_amount() const 9 { 10 return sales_amount; 11 } 12 double BookSale::get_revenue() const 13 { 14 return sales_amount * sales_price; 15 } 16 std::ostream& operator<<(std::ostream &out,const BookSale &item) 17 { 18 using std::left; 19 using std::setw; 20 out<<left; 21 out<<item.rb<<'\n' << setw(15) << "售价:" << item.sales_price << '\n'<< setw(15) << "销售数量:" << item.sales_amount << '\n'<< setw(15) << "营收:" << item.get_revenue(); 22 return out; 23 }
task2.cpp
1 #include"booksale.hpp" 2 #include<iostream> 3 #include<string> 4 #include<vector> 5 #include<algorithm> 6 bool compare_by_amount(const BookSale &x1,const BookSale &x2) 7 { 8 return x1.get_amount() >x2.get_amount() ; 9 } 10 void test() 11 { 12 using namespace std; 13 vector<BookSale> sales_lst; 14 int books_number; 15 cout<<"录入图书数量:"; 16 cin>>books_number; 17 cout<<"录入图书数量"<<endl; 18 for(int i=0;i<books_number;++i) 19 { 20 string name,author,translator,isbn; 21 float price; 22 cout<<string(20,'-')<<"第"<<i+1<<"本图书信息录入"<<string(20,'-')<<endl; 23 cout << "录入书名: "; 24 cin >> name; 25 cout << "录入作者: "; 26 cin >> author; 27 cout << "录入译者: "; 28 cin >> translator; 29 cout << "录入isbn: "; 30 cin >> isbn; 31 cout << "录入定价: "; 32 cin >> price; 33 Book book(name,author,translator,isbn,price); 34 float sales_price; 35 int sales_amount; 36 cout << "录入售价: "; 37 cin >> sales_price; 38 cout << "录入销售数量: "; 39 cin >> sales_amount; 40 BookSale record(book,sales_price,sales_amount); 41 sales_lst.push_back(record); 42 } 43 sort(sales_lst.begin(),sales_lst.end(),compare_by_amount); 44 cout<<string(20,'=')<<"图书销售统计"<<string(20,'=')<<endl; 45 for(auto &t:sales_lst) 46 { 47 cout<<t<<endl; 48 cout<<string(40,'-')<<endl; 49 } 50 } 51 int main() 52 { 53 test(); 54 }
运行结果截图

问题1:重载运算符<<
(1)找出<<被重载了几处?用于哪些类型?
答:2处。Book类和BookSale类
(2)找出使用重载<<输出对象的代码,写在下面。
答: 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;
}
for(auto &t:sales_lst)
{
cout<<t<<endl;
cout<<string(40,'-')<<endl;
}
问题2:图书销售统计
(1)"按销售数量降序排序",描述降序排序实现方式。
答:用了sort()函数和 bool compare_by_amount(const BookSale &x1,const BookSale &x2),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();});
任务三
taask3_1.cpp
1 #include<iostream> 2 class A 3 { 4 public: 5 A(int x0,int y0); 6 void display() const; 7 private: 8 int x,y; 9 }; 10 A::A(int x0,int y0):x{x0} ,y{y0} 11 { 12 } 13 void A::display() const 14 { 15 std::cout<<x<<", "<<y<<'\n'; 16 } 17 class B 18 { 19 public: 20 B(double x0,double y0); 21 void display() const; 22 private: 23 double x,y; 24 }; 25 B::B(double x0,double y0):x{x0},y{y0} 26 { 27 } 28 void B::display() const 29 { 30 std::cout<<x<<", "<<y<<'\n'; 31 } 32 void test() 33 { 34 std::cout<<"测试类A:"<<'\n'; 35 A a(3,4); 36 a.display(); 37 std::cout<<"\n测试类B: "<<'\n'; 38 B b(3.2,5.6); 39 b.display(); 40 } 41 int main() 42 { 43 test(); 44 }
task3_2.cpp
1 #include<iostream> 2 #include<string> 3 template<typename T> 4 class X 5 { 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 } 16 template<typename T> 17 void X<T>::display() 18 { 19 std::cout << x << ", " << y << '\n'; 20 } 21 void test() 22 { 23 std::cout << "测试1: 用int实例化类模板X" << '\n'; 24 X<int> x1(3, 4); 25 x1.display(); 26 std::cout << "\n测试2: 用double实例化类模板X" << '\n'; 27 X<double> x2(3.2, 5.6); 28 x2.display(); 29 std::cout << "\n测试3: 用string实例化类模板X" << '\n'; 30 X<std::string> x3("hello", "oop"); 31 x3.display(); 32 } 33 int main() 34 { 35 test(); 36 }
运行结果截图


任务四
pet.hpp
1 #pragma once 2 #include<iostream> 3 class MachinePet 4 { 5 public: 6 MachinePet(std::string nickname_):nickname{nickname_} 7 { 8 } 9 std::string get_nickname() const 10 { 11 return nickname; 12 } 13 virtual std::string talk() const=0; 14 private: 15 std::string nickname; 16 }; 17 class PetCat:public MachinePet 18 { 19 public: 20 PetCat(std::string nickname_):MachinePet(nickname_) 21 { 22 } 23 std::string talk() const override 24 { 25 return"miao wu~"; 26 } 27 private: 28 std::string nickname; 29 }; 30 class PetDog:public MachinePet 31 { 32 public: 33 PetDog(std::string nickname_):MachinePet(nickname_) 34 { 35 } 36 std::string talk() const override 37 { 38 return"wang wang~"; 39 } 40 private: 41 std::string nickname; 42 };
task4.cpp
1 #include<iostream> 2 #include<memory> 3 #include<vector> 4 #include"pet.hpp" 5 void test1() 6 { 7 std::vector<MachinePet *> pets; 8 pets.push_back(new PetCat("miku")); 9 pets.push_back(new PetDog("da huang")); 10 for(MachinePet *ptr:pets) 11 { 12 std::cout<<ptr->get_nickname()<<" says "<<ptr->talk()<<'\n'; 13 delete ptr; 14 } 15 } 16 void test2() 17 { 18 19 std::vector<std::unique_ptr<MachinePet>> pets; 20 pets.push_back(std::make_unique<PetCat>("miku")); 21 pets.push_back(std::make_unique<PetDog>("da huang")); 22 for(auto const &ptr: pets) 23 std::cout << ptr->get_nickname() << " says " << ptr->talk() << '\n'; 24 } 25 void test3() 26 { 27 const PetCat cat("miku"); 28 std::cout << cat.get_nickname() << " says " << cat.talk() <<'\n'; 29 const PetDog dog("da huang"); 30 std::cout << dog.get_nickname() << " says " << dog.talk() << '\n'; 31 } 32 int main() 33 { 34 std::cout << "测试1: 使用原始指针\n"; 35 test1(); 36 std::cout << "\n测试2: 使用智能指针\n"; 37 test2(); 38 std::cout << "\n测试3: 直接使用类\n"; 39 test3(); 40 }
运行结果截图

任务五
Complex.hpp
1 #pragma once 2 #include <iostream> 3 #include<string> 4 template<typename T> 5 class Complex 6 { 7 public: 8 Complex():real{0},imag{0} 9 { 10 11 } 12 Complex(T real_,T imag_):real{real_},imag{imag_} 13 { 14 } 15 16 Complex(const Complex<T>& other) 17 { 18 imag=other.imag; 19 real=other.real; 20 } 21 T get_real() const 22 { 23 return real; 24 } 25 T get_imag() const 26 { 27 return imag; 28 } 29 Complex<T>& operator+=(const Complex<T>& other) 30 { 31 real +=other.real; 32 imag +=other.imag; 33 return *this; 34 } 35 friend Complex<T> operator+(const Complex<T> &c1,const Complex<T> &c2) 36 { 37 return Complex<T>(c1.real+c2.real,c1.imag+c2.imag); 38 } 39 friend bool operator==(const Complex<T> &c1,const Complex<T> &c2) 40 { 41 return (c1.real==c2.real)&&(c2.imag==c1.imag); 42 } 43 friend std::istream& operator>>(std::istream& in,Complex<T> &c) 44 { 45 in>>c.real >>c.imag ; 46 } 47 friend std::ostream& operator<<(std::ostream& out,const Complex<T> &c) 48 { 49 if(c.imag>=0) 50 out<<c.real <<" + "<<c.imag<<'i'; 51 else 52 out<<c.real <<" - "<<-c.imag <<'i'; 53 return out; 54 } 55 private: 56 T imag,real; 57 };
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 &other就可以解决的,而是Complex<T> &other,这是之前的盲区,并对运算符重载有了新的认识,可以让一些运算符变得更加直观,输出更加简易。

浙公网安备 33010602011771号