EffectiveC++第六章继承和面向对象设计学习笔记


条款35: 使公有继承体现 "是一个" 的含义

    当写下类D("Derived" )从类B("Base")公有继承时,你实际上是在告诉编译器(以及读这段代码的人):类型D的每一个对象也是类型B的一个对象,但反之不成立;你是在说:B表示一个比D更广泛的概念,D表示一个比B更特定概念;你是在声明:任何可以使用类型B的对象的地方,类型D的对象也可以使用,因为每个类型D的对象是一个类型B的对象。相反,如果需要一个类型D的对象,类型B的对象就不行:每个D "是一个" B, 但反之不成立。

条款41: 区分继承和模板

    "类的行为" 和 "类所操作的对象的类型"之间的关系。如果T不影响行为,你可以使用模板。如果T影响行为,你就需要虚函数,从而要使用继承。

当然,"是一个" 的关系不是存在于类之间的唯一关系。类之间常见的另两个关系是 "有一个" 和 "用...来实现"。

条款36: 区分接口继承和实现继承

    作为类的设计者,有时希望派生类只继承成员函数的接口(声明);有时希望派生类同时继承函数的接口和实现,但允许派生类改写实现;有时则希望同时继承接口和实现,并且不允许派生类改写任何东西。

    纯虚函数最显著的特征是:它们必须在继承了它们的任何具体类中重新声明,而且它们在抽象类中往往没有定义。把这两个特征放在一起,就会认识到:定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。有时,声明一个除纯虚函数外什么也不包含的类很有用。这样的类叫协议类(Protocol class),它为派生类仅提供函数接口,完全没有实现。


复制代码
#include  < iostream >

using  std::cout;

using  std::endl;

using  std::cin;

class  Shape

{

public :

 
virtual   void  Draw()  =   0 ;

};

//  纯虚函数也可以有定义

void  Shape::Draw()

{

 cout 
<<   " Draw Shape "   <<  endl;

}

class  Rectangle :  public  Shape

{

public :

 
virtual   void  Draw();

};

void  Rectangle::Draw()

{

 cout 
<<   " Draw Rectangle "   <<  endl;

}

void  main()

{  

 Rectangle rect;

 rect.Draw();

 
//  可以通过这种方法调用纯虚函数的方法,如果定义的话.

 
// 这种用法一般没大的作用

 rect.Shape::Draw();

 cin.
get ();

}
复制代码

声明简单虚函数(即虚函数)的目的在于,使派生类继承函数的接口和缺省实现。

条款37: 决不要重新定义继承而来的非虚函数

"在一个类中声明一个非虚函数实际上为这个类建立了一种特殊性上的不变性"。Me:要保持 is a 的关系。


复制代码
#include  < iostream >

using  std::cout;

using  std::endl;

using  std::cin;

class  B {

public :

  
void  mf();

  
virtual   void  vir();

};

void  B::mf()

{

 cout 
<<   " B "   <<  endl;

}

void  B::vir()

{

 cout 
<<   " B virtual "   <<  endl;

}

class  D :  public  B

{

public :

 
void  mf();

 
virtual   void  vir();

};

void  D::mf()

{

 cout 
<<   " D "   <<  endl;

}

void  D::vir()

{

 cout 
<<   " D virtual "   <<  endl;

}

void  main()

{  

 D x;

 B 
*  pB  =   & x;

 pB
-> mf();

 pB
-> vir();

 D 
*  pD  =   & x;

 pD
-> mf();

 pD
-> vir();

 cin.
get ();

}
复制代码


/*

B

D virtual

D

D virtual

*/

条款38: 决不要重新定义继承而来的缺省参数值

Me:也可以这样说,决不要重新定义继承而来的带有缺省数值的虚函数。

目的:让我们从一开始就把问题简化。...如果忽视了本条款的建议,就会带来混乱。


复制代码
#include  < iostream >

using  std::cout;

using  std::endl;

using  std::cin;

namespace  Difa

{

enum  Color { RED, GREEN, BLUE };

}

//  一个表示几何形状的类

class  Shape 

{

public :

  
//  所有的形状都要提供一个函数绘制它们本身

 
virtual   void  draw(Difa::Color color  =   Difa::RED)  const   =   0 ;

};

class  Rectangle:  public  Shape 

{

public :

  
//  注意:定义了不同的缺省参数值 ---- 不好!

  
virtual   void  draw(Difa::Color color  = Difa::GREEN)  const ;

};

void  Rectangle::draw(Difa::Color color)  const

{

    cout 
<<  color  <<  endl;

}

class  Circle:  public  Shape 

{

public :

  
virtual   void  draw(Difa::Color color)  const ;

};

void  Circle::draw(Difa::Color color)  const

{

 cout 
<<  color  <<  endl;

}

void  main()



 Shape 
* ps;                       //  静态类型= Shape*

 Shape 
* pc  =   new  Circle;          //  静态类型= Shape*

 Shape 
* pr  =   new  Rectangle;       //  静态类型= Shape*

 pc
-> draw(Difa::RED);         //  调用 Circle::draw(RED)    0

 pr
-> draw(Difa::GREEN);      //  调用 Rectangle::draw(Difa::GREEN)  1

 pr
-> draw();         //  调用Rectangle::draw(RED)! 0 ,而不是 Difa::GREEN --->1

 cin.
get ();

}
复制代码

 

/*

0

1

0

*/

条款39: 避免 "向下转换" 继承层次

从一个基类指针到一个派生类指针 ---- 被称为 "向下转换",因为它向下转换了继承的层次结构。任何时候发现自己写出 "如果对象属于类型T1,做某事;但如果属于类型T2,做另外某事" 之类的代码,就要扇自己一个耳光。这不是C++的做法。是的,在C,Pascal,甚至Smalltalk中,它是很合理的做法,但在C++中不是。在C++中,要使用虚函数。不要在代码中随处乱扔条件语句或开关语句;让编译器来为你效劳。

糟糕的设计


复制代码
#include  < iostream >

#include 
< list >

using  std::list;

using  std::cout;

using  std::endl;

using  std::cin;

class  BankAccount 

{

public :

  
virtual   ~ BankAccount();

};

BankAccount::
~ BankAccount()

{

}

class  CheckingAccount:  public  BankAccount {

public :

  
void  creditInterest();     //  给帐户增加利息

};

void  CheckingAccount::creditInterest()

{

 cout 
<<   " Checking interest "   <<  endl;

}

class  SavingsAccount:  public  BankAccount 

{

public :

  
void  creditInterest();                 //  给帐户增加利息

};

void  SavingsAccount::creditInterest()

{

 cout 
<<   " savings interest! "   <<  endl;

}

void  main()



 list
< BankAccount *>  allAccounts;

 allAccounts.push_back(
new  CheckingAccount());

 allAccounts.push_back(
new  SavingsAccount);

 
for  (list < BankAccount *> ::iterator p  =  allAccounts.begin(); p  !=  allAccounts.end(); ++ p) 

 {

  
//  尝试将*p安全转换为SavingsAccount*;

  
//  psa的定义信息见下文

  
if  (SavingsAccount  * psa  =

    dynamic_cast
< SavingsAccount *> ( * p))

  {

   psa
-> creditInterest();

  }

  
//  尝试将它安全转换为CheckingAccount

  
else   if  (CheckingAccount  * pca  =

             dynamic_cast
< CheckingAccount *> ( * p))

  {

   pca
-> creditInterest();

  }

  
//  未知的帐户类型

  
else

  {

   cout 
<<   " Unknown account type! "   <<  endl;

  }

 }

  cin.
get ();

}
复制代码

 

   如果某个人在类层次结构中增加了一种新类型的帐户,但又忘了更新上面的代码,所有对它的转换就会失败。所以,处理这种可能发生的情况十分重要。大部分情况下,并非所有的转换都会失败;但是,一旦允许转换,再好的程序员也会碰上麻烦。为了消除向下转换,无论费多大工夫都是值得的,因为向下转换难看、容易导致错误,而且使得代码难于理解、升级和维护

 

条款40: 通过分层来体现 "有一个" 或 "用...来实现"

    某个类的对象成为另一个类的数据成员,从而实现将一个类构筑在另一个类之上,这一过程称为 "分层"(Layering)。条款35解释了公有继承的含义是 "是一个"。对应地,分层的含义是 "有一个" 或 "用...来实现"。

 

区分 "是一个" 和 "用...来实现"。

// Set中使用list的正确方法 me:List来实现Set


复制代码
template < class  T >

class  Set {

public :

 
bool  member( const  T &  item)  const ;

 
void  insert( const  T &  item);

 
void  remove( const  T &  item);

 
int  cardinality()  const ;

private :

 list
< T >  rep;                       //  表示一个Set

};
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自动控制节水灌溉技术的高低代表着农业现代化的发展状况,灌溉系统自动化水平较低是制约我国高效农业发展的主要原因。本文就此问题研究了单片机控制的滴灌节水灌溉系统,该系统可对不同土壤的湿度进行监控,并按照作物对土壤湿度的要求进行适时、适量灌水,其核心是单片机和PC机构成的控制部分,主要对土壤湿度与灌水量之间的关系、灌溉控制技术及设备系统的硬件、软件编程各个部分进行了深入的研究。 单片机控制部分采用上下位机的形式。下位机硬件部分选用AT89C51单片机为核心,主要由土壤湿度传感器,信号处理电路,显示电路,输出控制电路,故障报警电路等组成,软件选用汇编语言编程。上位机选用586型以上PC机,通过MAX232芯片实现同下位机的电平转换功能,上下位机之间通过串行通信方式进行数据的双向传输,软件选用VB高级编程语言以建立友好的人机界面。系统主要具有以下功能:可在PC机提供的人机对话界面上设置作物要求的土壤湿度相关参数;单片机可将土壤湿度传感器检测到的土壤湿度模拟量转换成数字量,显示于LED显示器上,同时单片机可采用串行通信方式将此湿度值传输到PC机上;PC机通过其内设程序计算出所需的灌水量和灌水时间,且显示于界面上,并将有关的灌水信息反馈给单片机,若需灌水,则单片机系统启动鸣音报警,发出灌水信号,并经放大驱动设备,开启电磁阀进行倒计时定时灌水,若不需灌水,即PC机上显示的灌水量和灌水时间均为0,系统不进行灌水。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值