条款32:确定你的public继承塑模出is-a关系
(1)public继承意味is-a,适用于base class身上的每一件事情一定也适用于derived classes身上,因为每一个devided class对象也是一个base class对象,但是每一个适用于devided class对象身上的每一件事情并不一定适用于base class。
条款33:避免遮掩继承而来的名称
(1)derived class内的名称会遮掩base classes内的名称,就像局部对象会遮掩全局对象一样
#include<iostream> using namespace std; class Base { private: int x; public: virtual void mf1() = 0; virtual void mf1(int a) { cout << "Base::mf1(int a):"<< a << endl; } virtual void mf2() { cout <<"Base::mf2:" << endl; } void mf3() { cout << "Base::mf3:" << endl; } void mf3(double a) { cout << "Base::mf3(double)" << a << endl; } }; class Derived:public Base { public: using Base::mf1; //让base class内名为mf1和mf3的所有东西在derived作用域内可见 using Base::mf3; virtual void mf1() { cout << "Derived::mf1:"<< endl; } void mf3() { cout << "Derived::mf3:"<<endl; } void mf4() { cout << "Derived::mf4" << endl; } }; void main() { Derived d; int x = 10 ; d.mf1(); d.mf1(x); d.mf2(); d.mf3(); d.mf3(x); }
如果在derived中不加using Base::mf1和using Base::mf3这两句话的话,derived中的mf1函数就会把Base类中的mf1函数遮掩掉,在调用d.mf1(x)和d.mf3(x)时就会编译报错,说找不到这样的函数,这是因为derived中的mf1等函数将base类中这样的函数遮掩掉了。
条款34:区别接口继承和实现继承
(1)身为class的设计者,有时候你希望derived class只继承成员函数的接口(也就是声明);有时候你希望derived classes同时继承函数的接口和实现,但有希望能够覆写(override)他们所继承的实现;有时你希望derived class同时继承函数的接口和实现,并且不允许覆写任何东西。为了实现这些功能可以用pure virtual实现只继承接口,impure virtual实现继承接口和实现并能覆写,一般的函数能够实现继承接口和实现但不能覆写。
(2)因为impure virtual实现继承接口和实现并能覆写,如果子类并不想使用默认继承来的impure virtual的话,该怎么解决呢?
#include<iostream> using namespace std; class Base { public: virtual void print() =0; //迫使子类必须实现该接口 }; void Base::print()//为子类提供一个可以调用的函数,但是必须是自己调用 { cout <<"Base::print()" << endl; } class Derived:public Base { public: void print() { Base::print(); //cout << "Derived::print()" << endl; } }; void main() { Base *pBase = new Derived; pBase->print(); }可以使用这种解决办法,为virtual函数提供一个默认的实现,因为子类必须要实现virtual函数,所以子类就不会默认的继承父类已经实现过的方法,子类可以调用父类提供的默认实现方法,但是调用该方法必须的是子类显式的调用,这样就能避免子类默认情况下继承父类的impure virtual函数的问题。
条款25:考虑virtual函数以外的其他选择
(1)藉由Non-Virtual Interface手法实现Template Method模式
#include<iostream> using namespace std; class GameCharacter { public: int healthValue() const { ..... //做一些事前工作 int retVal = doHealthValue(); ..... //做一些事后工作 } private: virtual int doHealthValue() const { ..... } };在这个类中,在healthValue函数中,调用了doHealthValue()这个虚函数,在调用这个虚函数之前做了一些初始化工作,在调用这个函数之后又做了一些初始化工作,这些都可以看做是骨架,doHealthValue()这个虚函数可以延后在子类中进行扩充,这就是设计模式中的template模式。
(2)藉由Funtion Pointers实现Strategy模式
这个方法的思想是,在这个类中保存另一个类的指针或者对象,通过保存的指针或对象调用该对象的方法,实现Strategy模式。可以将类中的virtual函数都替换为指针成员变量,这样比virtual更加灵活。不需要构造更多的类。
条款36:绝不重新定义继承而来的non-virtual函数
(1)如果重新定义了继承而来的non-virtual函数,就会出现遮掩现象(子类的方法这样父类的方法),并且non-virtual函数体现的是不变性,如果要在子类中重新定义这个函数,就说明这个函数是要在子类中改变的就不应该是non-virtual函数而应该是virtual函数。
#include<iostream> using namespace std; class B { public: void mf() { cout << "B::mf" << endl; } }; class D:public B { public: void mf() { cout << "D::mf" << endl; } }; void main() { D x; B *pB = &x; pB->mf(); D *pD = &x; pD->mf(); }这将因为指针的类型决定调用的是谁的函数的问题,而virtual函数不存在这样的问题。绝对不要重新定义继承而来的non-virtual函数。
条款37:绝不重新定义继承而来的缺省参数值
(1)因为virtual函数是动态绑定的,而缺省参数却是静态绑定的,所以函数调用的是子类的virtual函数,但是参数却是父类的缺省参数,即使子类中也定义了参数的缺省参数,使用的仍是父类的缺省参数。
#include<iostream> using namespace std; class Shape { public: enum ShapeColor{ Red,Green,Blue}; virtual void draw( ShapeColor color = Red ) const = 0; }; void Shape::draw(ShapeColor color) const { cout << color << endl; } class Rectangle: public Shape { public: virtual void draw( ShapeColor color = Green ) const { cout << color << endl; } }; class Circle :public Shape { public: virtual void draw( ShapeColor color) const { cout << color << endl; } }; void main() { Shape *ps1; ps1 = new Rectangle; ps1->draw(); delete ps1; Shape *ps2; ps2 = new Circle; ps2->draw(); delete ps2; }
(2)在遇到virtual函数的缺省参数时,可以使用下面的方法来替代设计。
#include<iostream> using namespace std; class Shape { public: enum ShapeColor{ Red,Green,Blue}; void draw( ShapeColor color = Red ) const { doDraw( color ); } private: virtual void doDraw( ShapeColor color ) const = 0; }; void Shape::doDraw(ShapeColor color) const { cout << color << endl; } class Rectangle: public Shape { public: virtual void doDraw( ShapeColor color ) const { cout << color << endl; } };
因为non-virtual函数绝对不被derived class 覆写,这个设计很清楚的使用color的缺省参数值。
条款38:通过复合塑模出has-a或“根据某物实现出”
(1)复合的意义和public继承完全不同。public继承是is-a,复合的意义是has-a,is-a体现的是子类是父类,has-a体现的是一个类中有另外一个类,但两个类不能相互替代,如果类A包含了类B,则类A可以调用类B的函数。
条款39:明智而审慎地使用private继承
(1)如果是类Dpublic继承类B,体现的是is-a,所以类D可以自动将类D转化成类B,如果是private继承,那么体现的将不是is-a,private体现的是类D和类B是没有必然的联系,只是类D想使用类B的一些方法或者函数,类D和类B之间没有必然的联系,所以呢类D将不会自动转化为类B。如下面程序:
#include<iostream> using namespace std; class B { public: int x; virtual void print() { cout << "B::print" << endl; } }; class D: private B { public: virtual void print() { cout << "D::print" << endl; } }; void callPrint( B &rb ) { rb.print(); } void main() { B b; D d; callPrint( b ); callPrint( d );//将会报错:error C2243: 'type cast' : conversion from 'class D *' to 'class B &' exists, but //is inaccessible(2)private继承和复合的作用都是使用某些类的方法,那么两者怎么取舍呢?首先,看下面的例子:}
#include<iostream> using namespace std; class Empty { }; class HoldAnInt { private: int x; Empty e;//应该不需要任何内存的 };会发现sizeof(HoldAnInt)>sizeof(int),因为大小为0的独立对象,c++官方勒令安插一个char(或其它如int)到空对象内,所以呢这样的话用private会比较节省空间,对于要节省空间很重要。
如果要是用private继承的话,如果子类就可以改写父类的一些方法,可能我们的本意只是想让子类使用这些方法,这将违背我们的意愿,所以这时候用组合还是比较好。
(3)C++也可以使用类中类
#include<iostream> using namespace std; class Empty { public: class test { public: void print() { cout << "Empty::test::print" << endl; } }; test t; }; void main() { Empty e; e.t.print(); }条款40:明智而审慎的使用多重继承
(1)多重继承容易导致歧义,以及钻石型多重继承,导致子类拥有多个父类的成员。