第五章 面向对象的编程风格(面向对象的编程思维)

5.2漫游:面向对象编程思维

 

程序编写:

 

    接下来,让我们实现一个三层的类体系,并借此引入c++语言中基本组成和支持面向对象编程方法的语法元素。我以Libmat这个抽象基类作为类体系中最根本的类。我从Libmat派生出Book,并从Book中派生出Audiobook.

    我们先限定接口只有一个constructor一个destructor和一个print()函数,我为每个member function加上一写程序代码,输出信息表示它们的存在,让我们得以跟踪程序的行为。
    默认情形下,member fuction的解析(resolution)皆在编译时静态进行。若要另其在运行时动态进行,我们就得在它的声明前加上关键字virtual。Libmat的声明表示,其

 

destructor和print()皆为virtual(虚函数)

class Libmat
{
public:
Libmat()
{cout<<"Libmat::Libmat()
 default constructor!\n";}
virtual ~Libmat()
{cout<<"Libmat::~Libmat()
 default constructor!\n";}
virtual void print() const
{cout<<"Libmat::print()--i an a Libmat object!\n";}
}; 
现在我们定义一个non-member fuction print(),它接受一个参数,其形式为 const Libmat reference:
void print(const Libmat &mat)
{
cout<<"in global print():about to print mat.print()\n";
//下一行会依据mat实际指向的对象,解析应该执行哪一个print() member function.
mat.print();

}

 

我们在main()程序中重复调用print(),并依次将Libmat对象,BOOK对象,audiobook对象当做参数传给它。每次print()被执行,都会依据mat实际所指的对象,在Libmat,Book,Audiobook三者之间挑选正确的print()member function加以调用,第一次调用操作像下面这样:

cout<<'\n'<<"Creating a Libmat object to print()\n";
Libmat libmat;
print(libmat);
一下是跟踪结果:
creating a Libmat object to print()
//构造Libmat libmat
Libmat::Libmat() default constructor!
//处理print(libmat)
in global print():about to print mat.print()
Libmat::print()--i am a libmat object!
//析构libmat libmat
Libmat::~Libmat() destructor!
希望你不会对此感到任何惊讶。default constructor 的调用时紧跟在libmat的定义行为后。而在print()中,mat.print()会被解析为Libmat::print().接踵而来的便是Libmat destructor的调用。但是当我将Book对象传入print(),情况有点令人惊讶:

cout<<"\n"<<"creating a book object to print()\n";
Book b("the castle","franz kafka");
print(b);
以下便是经过注释的跟踪结果:
creating a book object to print()
//构造Book b
Libmat::Libmat() default constructor!
Book::Book(the castle,franz,kafka)constructor
//处理print(b)
in global print():about to print mat.print()
Book::print()--i am a book object!
my title is:the castle
my auther is:franz kafka
//析构Book b
Book::~Book() destructor!
Libmat::~Libmat()destructor!

第一个印象是,通过mat.print()所进行的虚拟调用(virtual invocation)操作的确有效。被调用的函数是Book::print()而非Libmat::print().第二个令人感兴趣的事是,当程序定义出一个派生对象,基类和派生类的constructor都会被执行。(当派生对象被销毁,基类和派生类的destructor也都会被执行。但次序颠倒)

 

 

   我们该如何实现派生类Book呢?

   

为了清楚标示这个类是这个类乃是继承自一个已存在的类,其名称之后必须接一个冒号(:),然后紧跟着关键字public和基类的名称:

class Book:public Libmat{

public:
Book(const string &title,const string &auther)
:_title(title),_auther(auther)
{
cout<<"Book:Book("<<_title<<", "<<_auther<<")
constructor!\n";
}
virtual ~Book(){
cout<<"Book::Book() destructor!\n";
}
virtual void print()const{
cout<<"Book::print()--I am a Book object!\n";
<<"My title is:"<<_title<<'\n'
<<"My auther is:"<<_auther<<endl;
}
const string& title()
const {return _title;}
const string auther()
const {return _auther;}

protected:
string _title;
string _auther;

};
    Book中的print()覆盖了(override)Libmat的print().这也正是mat.print()所调用的函数。title()和auther()是两个所谓的访问函数,都是non-virtual inline函数。过去我们不曾介绍关键字protected是的,被声明为protected所有成员函数都可以被派生类直接访问,除此(派生类)以外,都不得直接访问protected成员。

    接下来,我从Book类派生出一个更特殊的audiobook类。audiobook除了拥有标题和作者,还有播讲者。在查看其实现之前,先让我把audiobook对象传给print():
cout<<"\n"<<"creating an audiobook object to print()\n";
audiobook ab("man without qualities","robert musil","kenneth meyer");
print(ab);
我们应该预期出现什么样的跟踪结果呢?我们应该预期:(1)通过mat.print()调用的是audiobook::print()(2)ab的构造过程乃是依次调用Libmat,Book,audiobook的constructor。一下即是跟踪结果:
creating a book object to print()
Libmat::Libmat() default constructor!
Book::Book(the castle,franz,kafka)constructor
//处理print(b)
in global print():about to print mat.print()
Book::print()--i am a book object!
my title is:the castle
my auther is:franz kafka
//析构Book b
Book::~Book() destructor!
Libmat::~Libmat()destructor!

该如何实现audiobook这个派生类呢?我们只需要把焦点放在audiobook与其基类book的不同之处——也就是print()——即可。当然,我们还必须提供audiobook播讲者姓名,以及这个类的constructor和destructor。至于book类所提供的各项数据及操作函数,均可被audiobook直接使用,仿佛它们本来便是由audiobook定义似的。

class audiobook:public book{

public:

audiobook(const string &title,const striing &auther,const string &narrator)

:book(title,auther),_narrator(narrator)

{

cout<<"audiobook::audiobook("_title<<","<<_auther<<","<<_narrator<<")constructor\n;

}

~audiobook()

{cout<<"audiobook::~audiobook() destructor!\n";}

 

virtual void print() const{

cout<<"audiobook::print()—i am an audiobook object!\n"

//注意,以下直接访问继承而来的

//打他member:_author,_title

<<"my title is:"<<_title<<'\n'

<<"my author is:"<<_narrator<<endl;}

const string& narrator() const{return _narrator;}

protected

string _narrator;

};

使用派生类时不必刻意区分“继承而来的成员”和“自身定义的成员”。两者的使用完全透明:

int main()

{

audiobook ab("mason and dixon","thomas pynchon","edwin leonard");

cout<<"the title is"<<ab.title()<<'\n'

<<"the author is:"<<ab.author()<<'\n'

<<"the narrator is:"<<ab.narrator()<<endl;}

 

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值