Effectiv C++ (06)

来源:互联网 发布:珠海设计院使用软件 编辑:程序博客网 时间:2024/06/08 11:15

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

Class Shape

{

Public:

Virtual void draw() const = 0;

Virtual void error(const string &msg);

Int objectID() const;
};

Class Rectangle : public Shape

{

};

Class Ellipse:public Shape

{

};

成员函数的接口总是被继承

纯虚函数:

         必须被继承了的子类实现

         声明virtualfunc = 0;是为了子类只继承接口

Shape *ps = new Shape;    Error

Shape *ps1 = new Rectangle; OK

Ps1->draw();

Shape *ps2 = new Ellipse;

Ps2->draw();

Ps1->Shape::draw();   调用Shape::draw

Ps2->Shape::draw();   调用Shape::draw

 

声明非virtual函数是令子类继承函数的接口以及一份强制性的实现。任何子类不不应该尝试改变其实现方式。不应该在子类中重定义。

纯虚函数:只继承接口

虚函数:  继承接口和一份缺省实现

普通函数:继承接口和一份强制实现

常犯的错误:

         将所有函数都声明为普通成员函数(非虚、非纯虚),除非它不会作为父类被继承

         将所有成员函数声明为virtual,除非它是interfaceclass

接口继承和实现继承不一样

条款35:考虑virtual函数以外的其他选择

Class GameCharacter

{

Public:

Int healthValue() const

{

    Int retVal = doHealthValue();

    Return retVal;

}

Private:

Virtual int doHealthValue() const

{

}

};

通过非虚函数间接调用private virtual函数。NVI(non-virtual interface)

条款36:绝不重新定义继承而来的非虚函数

如果要改,说明子类“is-a”父类,不成立。要么不能public继承,要么就需要将该方法声明为virtual。

对于析构函数,由于每一个类的析构过程与父类不尽相同,因此需要声明为virtual

条款37:绝不重新定义继承而来的缺省参数值

假设继承而来的函数分为:虚函数和非虚函数。由于不能重写非虚函数,因此框定:继承一个带有缺省参数的virtual函数。

virtual函数是动态绑定的,而缺省参数值是静态绑定的!

对象的静态绑定,它在程序中被声明时所采用的类型。

Class Shape

{

Public:

Enum ShapeColor{Red, Green, Blue};

Virtual void draw(ShapeColor color = Red) const = 0;

};

 

Class Rectangle : public Shape

{

Public:

Virtual void draw(ShapeColor color = Green) const = 0;

};

Class Circle : public Shape

{

Public:

Virtual void draw(ShapeColor color) const = 0; // 不带缺省参数

};

// 静态绑定为Shape类型

Shape *ps;

Shape *pc = new Circle;

Shape *pr = new Rectangle;

// 动态绑定

Ps = pc;   // Circle

Ps = pr;   // Rectangle

 

Pc->draw(Shape::Red);    // 调用Circle::draw(Shape::Red)

Ps->draw(Shape::Red);    // 调用Rectangle::draw(Shape::Red)

 

//带有缺省参数。缺省参数静态绑定

Pr->draw();    // 调用Rectangle::draw(Shape::Red),此例中pr是Rectangle类型,因此会调//用Rectangle::draw,然而其缺省参数应该是Green,但由于pr的静态类型是Shape,所以//缺省值来自于Shape而不是Rectangle。结果比较诡异,各贡献一半!

//引用,结果也一样

缺省参数值是静态绑定。即什么类型(Shape)的指针或引用,被赋予那个类型(Shape)的缺省值,而不考虑其真实类型(Circlr Rectangle).

条款38:通过复合模塑出“has-a”或“根据某物实现出”

复合:has-a或is-implement-in-terms-of(根据某物实现出)

复合:在应用域:has-a;在实现域:根据某物实现出

条款39:明智而审慎的使用private继承

如果是private继承,编译器不会自动将一个子类转换为一个父类对象,这与public继承不同

private继承自所有父类的东西,在子类都是private的

如果classD私有继承classB,目的是采用classB内已经备妥的某些特性

private继承纯粹是一种实现技术。

private继承意味着只有实现部分被继承,接口部分被略去。

private继承意味着“根据某物实现出”

尽可能使用复用,必要时才使用private。何时必要?当protected、virtual成员牵涉进来时。

对于class内部为空的,sizeof(ClassEmty)并不是0,而是被安插了1个char

Class Empty

{

};

Class HoldsAnInt

{

Int num;

Empty e;

};

//Sizeof(HoldsAnInt)可能是8

Class HoldsAnInt : private Empty

{

    Int num;

};

//Sizeof(HoldsAnInt) = 4

条款40:明智而审慎的使用多重继承

Class BorrowableItem

{

Public:

    Void checkOut();

};

Class ElectronicGadget

{

Private:

    Bool checkOut()const;

};

Class MP3Player:

Public BorrowableItem,

Pubilc ElectronicGadget

{

};

MP3Player mp;

Mp.checkOut();    // 歧义

在看到是否有函数可用之前,C++首先确认这个函数对此调用而言是最佳匹配。

找出最佳匹配函数之后,才检验其可用性(即接口访问限制:public、private、const…)上例中两个函数有相同的匹配程度,造成了歧义。没有所谓的最佳匹配,因此可用性还没有检查

明白指出调用哪一个:

mp.BorrowableItem::chechOut();

Class File

{};

Class InputFile: public File{};

Class OutputFile:public File{};

Class IOFile: public InputFile, public OutputFile{};

 

 

如果子类继承的父类有一个共同的祖先:C++缺省的做法是保留两份祖先的成员。

如果想让子类只有一份祖先的成员,需要将祖先设为virtual base class

 

Class File

{};

Class InputFile: virtual public File{};

Class OutputFile:virtual public File{};

Class IOFile: public InputFile, public OutputFile{};

 

访问virtual继承的代价大!需要为virtual继承付出代价

virtual继承的初始化复杂、不直观:责任由继承体系中最底层的class(IOFile)负责

建议:

非必要不要使用virtual继承

如果必须使用virtual继承,应避免在其中放置数据(不用担心因初始化和赋值而带来的诡异事)。

virtual继承会增加大小、速度、初始化(赋值)复杂度

原创粉丝点击