实验5

一、实验任务1

源代码publisher.hpp

 1 #pragma once
 2 #include<string>
 3 class Publisher{
 4 public:
 5     Publisher(const std::string &name_="");
 6     virtual ~Publisher()=default;
 7 public:
 8     virtual void publish() const=0;
 9     virtual void use() const=0;
10 protected:
11     std::string name;
12 };
13 class Book:public Publisher{
14 public:
15     Book(const std::string &name_="",const std::string &author_="");
16 public:
17     void publish() const override;
18     void use() const override;
19 private:
20     std::string author;
21 };
22 class Film:public Publisher{
23 public:
24     Film(const std::string &name_="",const std::string &director_="");
25 public:
26     void publish() const override;
27     void use() const override;
28 private:
29     std::string director;
30 };
31 class Music:public Publisher{
32 public:
33     Music(const std::string &name_="",const std::string &artist_="");
34 public:
35     void publish() const override;
36     void use() const override;
37 private:
38     std::string artist;
39 };
publisher.hpp

源代码publisher.cpp

 1 #include<iostream>
 2 #include<string>
 3 #include"publisher.hpp"
 4 Publisher::Publisher(const std::string &name_):name{name_}{}
 5 Book::Book(const std::string &name_,const std::string &author_):
 6           Publisher{name_},author{author_}{}
 7 void Book::publish() const{
 8     std::cout<<"Publishing book《"<<name<<"》by "<<author<<'\n';     
 9 }
10 void Book::use() const{
11     std::cout<<"Reading book《"<<name<<"》by "<<author<<'\n'; 
12 }
13 Film::Film(const std::string &name_,const std::string &director_):
14           Publisher{name_},director{director_}{}
15 void Film::publish() const{
16     std::cout<<"Publishing film <"<<name<<"> directed by "<<director<<'\n';     
17 }
18 void Film::use() const{
19     std::cout<<"Watching film <"<<name<<"> directed by "<<director<<'\n'; 
20 }
21 Music::Music(const std::string &name_,const std::string &artist_):
22           Publisher{name_},artist{artist_}{}
23 void Music::publish() const{
24     std::cout<<"Publishing music <"<<name<<"> by "<<artist<<'\n';     
25 }
26 void Music::use() const{
27     std::cout<<"Listening to music <"<<name<<"> by "<<artist<<'\n'; 
28 }
publisher.cpp

源代码task1.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

 ps:在DevC中使用智能指针会出现版本不支持的报错问题,在vs2022中可以正常运行。

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

答:Publisher类是Book/Film/Music类的基础,并且在Publisher类中具有publish()和use()两个纯虚函数。

image

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

答:编译不能通过,因为Publisher是抽象类,抽象类不能被实例化为对象,因为抽象类中有未定义的操作。

image

问题2:纯虚函数与接口继承
(1) Book 、 Film 、 Music 必须实现哪两个函数才能通过编译?请写出其完整函数声明。

答:Book、Film、Music必须实现publish()和use()两个函数。

image

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

答:Film类中的重载的成员函数定义时与基类中的函数不匹配,无法进行对于基类中虚函数的重写。

image

问题3:运行时多态与虚析构
(1)在 test1() 里, for (Publisher *ptr : v) 中 ptr 的声明类型是什么?

答:ptr的声明类型是指向基类Publisher的指针类型。

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

答:Book*、Film*、Music*。

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

答:基类Publisher的析构函数声明为virtual可以使基类的指针指向派生类对象从而可以删除派生类对象,防止只析构基类而不析构派生类的情况。

删除virtual,可以正常运行,但派生类没有被正确析构,会出现内存泄漏的问题。

二、实验任务2

源代码book.hpp

 1 #pragma once
 2 #include<string>
 3 class Book{
 4 public:
 5     Book(const std::string &name_,const std::string &author_,
 6          const std::string &translator_,const std::string &isbn_,
 7          double price_);
 8     friend std::ostream& operator<<(std::ostream &out,const Book &book);
 9 private:
10     std::string name;
11     std::string author;
12     std::string translator;
13     std::string isbn;
14     double price; 
15 };
book.hpp

源代码book.cpp

 1 #include<iomanip>
 2 #include<iostream>
 3 #include<string>
 4 #include"book.hpp"
 5 Book::Book(const std::string &name_,const std::string &author_,
 6            const std::string &translator_,const std::string &isbn_,
 7            double price_):name{name_},author{author_},translator{translator_},
 8            isbn{isbn_},price{price_}{}
 9 std::ostream& operator<<(std::ostream &out,const Book &book){
10     using std::left;
11     using std::setw;
12     out<<left;
13     out<<setw(15)<<"书名:"<<book.name<<"\n"
14        <<setw(15)<<"作者:"<<book.author<<"\n"
15        <<setw(15)<<"译者:"<<book.translator<<"\n"
16        <<setw(15)<<"ISBN:"<<book.isbn<<"\n"
17        <<setw(15)<<"定价:"<<book.price;
18     return out;
19 }
book.cpp

源代码booksale.hpp

 1 #pragma once
 2 #include<string>
 3 #include"book.hpp"
 4 class BookSale{
 5 public:
 6     BookSale(const Book &rb_,double sales_price_,int sales_amount_);
 7     int get_amount() const;
 8     double get_revenue() const;
 9     friend std::ostream& operator<<(std::ostream &out,const BookSale &item);
10 private:
11     Book rb;
12     double sales_price;
13     int sales_amount;
14 };
booksale.hpp

源代码booksale.cpp

 1 #include<iomanip>
 2 #include<iostream>
 3 #include<string>
 4 #include"booksale.hpp"
 5 BookSale::BookSale(const Book &rb_,double sales_price_,
 6                    int sales_amount_):rb{rb_},sales_price{sales_price_},
 7                    sales_amount{sales_amount_}{}
 8 int BookSale::get_amount() const{
 9     return sales_amount;
10 }
11 double BookSale::get_revenue() const{
12     return sales_amount*sales_price;
13 }
14 std::ostream& operator<<(std::ostream &out,const BookSale &item){
15     using std::left;
16     using std::setw;
17     out<<left;
18     out<<item.rb<<'\n'
19        <<setw(15)<<"售价:"<<item.sales_price<<'\n'
20        <<setw(15)<<"销售数量:"<<item.sales_amount<<'\n'
21        <<setw(15)<<"营收:"<<item.get_revenue();
22     return out;
23 }
booksale.cpp

源代码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     return x1.get_amount()>x2.get_amount();
 8 }
 9 void test(){
10     using namespace std;
11     vector<BookSale> sales_lst;
12     int books_number;
13     cout<<"录入图书数量:";
14     cin>>books_number;
15     cout<<"录入图书销售记录:"<<endl;
16     for(int i=0;i<books_number;i++){
17         string name,author,translator,isbn;
18         float price;
19         cout<<string(20,'-')<<""<<i+1<<"本图书信息录入"<<string(20,'-')<<endl;
20         cout<<"录入书名:";cin>>name;
21         cout<<"录入作者:";cin>>author;
22         cout<<"录入译者:";cin>>translator;
23         cout<<"录入isbn:";cin>>isbn;
24         cout<<"录入定价:";cin>>price;
25         Book book(name,author,translator,isbn,price);
26         float sales_price;
27         int sales_amount;
28         cout<<"录入售价:";cin>>sales_price;
29         cout<<"录入销售记录:";cin>>sales_amount;
30         BookSale record(book,sales_price,sales_amount);
31         sales_lst.push_back(record); 
32     }
33     sort(sales_lst.begin(),sales_lst.end(),compare_by_amount);
34     cout<<string(20,'=')<<"图书销售统计"<<string(20,'=')<<endl;
35     for(auto &t:sales_lst){
36         cout<<t<<endl;
37         cout<<string(40,'-')<<endl;
38     } 
39 }
40 int main(){
41     test();    
42 }
task2.cpp

运行成果展示

image

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

答:两处,一处在Book类中,另一处在BookSale类中。

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

image

 

image

问题2:图书销售统计
(1)图书销售记录"按销售数量降序排序",代码是如何实现的?
答:通过排序函数sort(sales_lst.begin(),sales_lst.end(),compare_by_amount);,并且规定比较排序compare的原则是降序。

image

(2)拓展(选答*):如果使用lambda表达式,如何实现"按销售数量降序排序"?

答:lambda表达式是在调用或者作为函数参数传递的位置处定义匿名函数对象的方法。在这里使用lambda表达式可以省去多定义一个compare函数。

image

三、实验任务3

源代码task3_1.cpp

 1 #include<iostream>
 2 class A{
 3 public:
 4     A(int x0,int y0);
 5     void display() const;
 6 private:
 7     int x,y;
 8 }; 
 9 A::A(int x0,int y0):x{x0},y{y0}{}
10 void A::display() const{
11     std::cout<<x<<","<<y<<'\n';
12 }
13 class B{
14 public:
15     B(double x0,double y0);
16     void display() const;
17 private:
18     double x,y;
19 };
20 B::B(double x0,double y0):x{x0},y{y0}{}
21 void B::display() const{
22     std::cout<<x<<","<<y<<'\n';
23 }
24 void test(){
25     std::cout<<"测试类A:"<<'\n';
26     A a(3,4);
27     a.display();
28     std::cout<<"测试类B:"<<'\n';
29     B b(3.2,5.6);
30     b.display();
31 }
32 int main(){
33     test();
34 }
task3_1.cpp

运行成果展示

image

源代码task3_2.cpp

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

运行成果展示

image

四、实验任务4

源代码pet.hpp

 1 #pragma once
 2 #include<string>
 3 class MachinePet{
 4 public:
 5     MachinePet(const std::string &nickname);
 6     virtual std::string get_nickname() const=0;
 7     virtual std::string talk() const=0;
 8     virtual ~MachinePet()=default;
 9 protected:
10     std::string nickname;
11 };
12 class PetCat:public MachinePet{
13 public:
14     PetCat(const std::string &catname);
15     std::string get_nickname() const override;
16     std::string talk() const override;
17 };
18 class PetDog:public MachinePet{
19 public:
20     PetDog(const std::string &dogname);
21     std::string get_nickname() const override;
22     std::string talk() const override;
23 };
pet.hpp

源代码pet.cpp

 1 #include<iostream>
 2 #include<string>
 3 #include"pet.hpp"
 4 MachinePet::MachinePet(const std::string &nickname):nickname{nickname}{}
 5 PetCat::PetCat(const std::string &catname):MachinePet(catname){}
 6 std::string PetCat::get_nickname() const{
 7     return nickname;
 8 }
 9 std::string PetCat::talk() const{
10     return "miao wu~";
11 }
12 PetDog::PetDog(const std::string &dogname):MachinePet(dogname){}
13 std::string PetDog::get_nickname() const{
14     return nickname;
15 }
16 std::string PetDog::talk() const{
17     return "wang wang~";
18 }
pet.cpp

源代码task4.cpp

 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     for(MachinePet *ptr:pets){
10         std::cout<<ptr->get_nickname()<<" says "<<ptr->talk()<<'\n';
11         delete ptr;
12     }
13 }
14 void test2(){
15     std::vector<std::unique_ptr<MachinePet>> pets;
16     pets.push_back(std::make_unique<PetCat>("miku"));
17     pets.push_back(std::make_unique<PetDog>("da huang"));
18     for(auto const &ptr:pets)
19         std::cout<<ptr->get_nickname()<<" says "<<ptr->talk()<<'\n';
20 }
21 void test3(){
22     const PetCat cat("miku");
23     std::cout<<cat.get_nickname()<<" says "<<cat.talk()<<'\n';
24     const PetDog dog("da huang");
25     std::cout<<dog.get_nickname()<<" says "<<dog.talk()<<'\n';
26 }
27 int main(){
28     std::cout<<"测试1:使用原始指针\n";
29     test1();
30     std::cout<<"\n测试2:使用智能指针\n";
31     test2();
32     std::cout<<"\n测试3:直接使用类\n";
33     test3(); 
34 }
task4.cpp

运行成果展示

image

五、实验任务5

源代码Complex1.hpp

 1 #pragma once
 2 template<typename T>
 3 class Complex{
 4 public:
 5     Complex():real{0},imag{0}{}
 6     Complex(T real,T imag):real{real},imag{imag}{}
 7     Complex(const Complex<T> &c){
 8     real=c.real;
 9     imag=c.imag;
10     }
11     T get_real() const{
12     return real;
13     }
14     T get_imag() const{
15     return imag;
16     }
17     Complex<T>& operator+=(const Complex<T> &c){
18     real+=c.real;
19     imag+=c.imag;
20     return *this;
21     }
22     template<typename T1>
23     friend Complex<T1> operator+(const Complex<T1> &c1,const Complex<T1> &c2);
24     template<typename T1>
25     friend bool operator==(const Complex<T1> &c1,const Complex<T1> &c2);
26     template<typename T1>
27     friend std::istream& operator>>(std::istream &cin,Complex<T1> &c);
28     template<typename T1>
29     friend std::ostream& operator<<(std::ostream &cout,const Complex<T1> &c);
30 private:
31     T real,imag;
32 };
33 template<typename T1>
34 Complex<T1> operator+(const Complex<T1> &c1,const Complex<T1> &c2){
35     return Complex<T1>(c1.real+c2.real,c1.imag+c2.imag);
36 }
37 template<typename T1>
38 bool operator==(const Complex<T1> &c1,const Complex<T1> &c2){
39     if(c1.real==c2.real&&c1.imag==c2.imag)
40         return true;
41     else
42         return false;
43 }
44 template<typename T1>
45 std::istream& operator>>(std::istream &cin,Complex<T1> &c){
46     cin>>c.real>>c.imag;
47     return cin;
48 }
49 template<typename T1>
50 std::ostream& operator<<(std::ostream &cout,const Complex<T1> &c){
51     cout<<c.real;
52     if(c.imag<0)
53         cout<<c.imag<<"i";
54     else
55         cout<<"+"<<c.imag<<"i";
56     return cout;
57 }    
Complex1.hpp

源代码task5.cpp

 1 #include<iostream>
 2 #include"Complex1.hpp"
 3 void test1(){
 4     using std::cout;
 5     using std::boolalpha;
 6     Complex<int> c1(2,-5),c2(c1);
 7     cout<<"c1="<<c1<<'\n';
 8     cout<<"c2="<<c2<<'\n';
 9     cout<<"c1+c2="<<c1+c2<<'\n';
10     c1+=c2;
11     cout<<"c1="<<c1<<'\n';
12     cout<<boolalpha<<(c1==c2)<<'\n';
13 }
14 void test2(){
15     using std::cin;
16     using std::cout;
17     Complex<double> c1,c2;
18     cout<<"Enter c1 and c2: ";
19     cin>>c1>>c2;
20     cout<<"c1="<<c1<<'\n';
21     cout<<"c2="<<c2<<'\n';
22     const Complex<double> c3(c1);
23     cout<<"c3.real="<<c3.get_real()<<'\n';
24     cout<<"c3.imag="<<c3.get_imag()<<'\n';
25 }
26 int main(){
27     std::cout<<"自定义类模板Complex测试1:\n";
28     test1();
29     std::cout<<"\n自定义类模板Complex测试2:\n";
30     test2();
31 }
task5.cpp

运行成果展示

image

 

实验总结:

一、本次实验实践了继承的功能,但我在之后完成任务4时的时候还是出现了一些问题:

1.在基类中的nickname成员需要使用protected保护类型,这样派生类才可以访问。

2.派生类类中如果有与基类相同的成员(比如nickname),不用重复写了,但可以添加派生类特有的成员(private型)。

3.如果基类中纯虚函数加了const,在对其重写时也要加上const保持一致。

二、本次实验还实践了类模板和运算符重载,在任务5出现的一些问题:

1.一开始我为了看起来整齐就把类模板的声明和实现分别放在.hpp和.cpp的文件中,结果一直出现在主函数中找不到类模板函数实现的链接报错问题,最好把它们都放到一个.hpp中,因为编译器处理模板实例化时除了需要模板的定义,还需要模板的实现体,所以模板类的声明定义不分离。

2.在写友元函数时要重新写一个模板,比如 template<typename T1>,并且在每一个友元函数实现时都要加上。

posted @ 2025-12-15 09:45  鱼籽不煮粥  阅读(10)  评论(0)    收藏  举报