对继承的理解。
我的发帖。
http://bbs.csdn.net/topics/392047906
我的体会:
0) 在做前端的时候发现一个继承的适合场景,就是母模板和子页面的关系。用继承比组合恰当。
1) 继承的根本目的是为了对要解决的问题进行更好的建模
2)继承的目的不是复用,从某种意义上来说,恰恰是为了处理不能复用的场景。所以才有继承。复用只是大多数优秀模型的一个必然结果而已。
3)从底层实现来说,就是把原来一个含判断多分支的函数,根据判断分开放到不同的方法代码区块,但使用同一个函数名称。
4)继承而不是用参数来扩展,从某种意义上来说,就是符合了开闭原则,只有对项目有要求,才会知道开闭原则的重要,参数毕竟不能无限考虑到后期扩展。
这样,当有以新方法时添加时,不用添加到原来大一统的函数里。而放到另外一个i代码区块(新建立一个派生类)。对修改关闭。
4)很多情况组合会好过继承。不是继承不好,而是has-a关系很容易误认为is-a关系(语言习惯问题,等等)。如,小明是一个工程师。看起来是is-a。其实正确的说法是,小明是一个含有工程师工作的人。明显的2个抽象对象。人和职业。
工程师对于人来说并不是绝对稳定的。人可以变换工作。但是对于职业来说却是稳定的。工程师天生就是一种职业。
5)建模过程需要一组类实现更高的抽象,那么使用多态。但注意要把抽象精小化。这样可以使一组类包含一个抽象,而不是整组类都实现此抽象。来达到更低的耦合。
6)最近感觉继承中的基类,根本不应该有private。基类就应该纯虚类或者带protect数据。要不private数据到底什么意思?基类设置private数据的情况自己还没碰到过。
c++真的必须阉割才能安全使用。
还有一篇,感觉写的很好的文章。
http://www.cnblogs.com/shuhari/archive/2009/06/04/inherit_thinking.html
从软件的发展过程来解释继承。当前都是分层开发,数据库都是关系型数据库。表与表之间基本都是交互关系。而不是继承关系。所以继承的使用,当前来说,很少是匹配适用的。
除非是的is a关系。绝大多数都用组合或其他模式吧。
看 编程规范 一书中
37条款。
再一次印证了继承不是为了重用。
一直在想如何正确使用is-a,书中给了非常正确的解释,不要解读为是一个。要解读为 行为像一个。 比如下面的例子。
销售,工程师,和职工,不能说小张是一个工程师。就用继承,而应该说 小张的行为像一个工程师。明显小张的行为像一个工程师是一个错误的语义,小张行为像一个人。而不是工程师。
#include<iostream> #include <vector> using namespace std; //1)新类和原类,计算工资不同,但为了新类和原类统一行为.建立基类,并设置为虚方法,为多态准备. //2)修改原类为派生类,新加一个派生类. // 并把我们认为实际中,确实是公有并且固定的方法(不会因为派生类不同而有所不同,并且预知以后也不可能会有多态行为),设置为实方法.提供公用. //3)因此我们的目的是为了统一行为,手段是继承和虚方法,最终多写了代码,增加了复杂度,但安慰奖是,新加类可以复用基类的实方法. // 因此,继承的来源之一是不同类有不同行为,但要达到统一的目的,使用了多态的技术,复用是安慰奖.复用不是目的!!! // 继续变动.如果一个员工从saler变成了provider呢?sales linson("linson",20000,50000);改为provider linson("linson",20000); // 但是如果很多地方使用了这条sales linson("linson",20000,50000)语句呢.一个一个改.但总归还是怕会有遗漏的.不是一个好的解决问题方案. // 是否有更好的处理方法.首先想到,应该就是如何把计算工资的方法,和对象分离出来.但一想.员工类型你没变过来啊.员工类型和工资分离.那我要员工类型做什么? // 进而怀疑.我要继承做什么? // 这里是否不应该用继承?但是很明显,毫无压力的is-a关系啊.对,类和类之间确实是毫无争议的is-a关系. // 但是派生类的对象.存在转变为另外派生类对象的可能.这导致了毫无争议的继承场景,变成了让人犯嘀咕的对象. class iWorker { public: iWorker(const string& name); virtual int Salary()=0; void showSalary(ostream& os); inline string getName(); virtual ~iWorker(); protected: string name; private: iWorker(); }; void iWorker::showSalary(ostream& os) { os<<"name:"<<name<<". "<<"Salary:"<<Salary()<<"."<<endl; } inline string iWorker::getName() { return name; } iWorker::iWorker(const string& _name):name(_name){} iWorker::~iWorker(){} class sales:public iWorker { public: sales(const string& _name,int _base,int sum); inline string getName(); int Salary(); //void showSalary(ostream&); private: //string name; int baseSalary; int saleSum; }; int sales::Salary() { return baseSalary+saleSum*0.1; } sales::sales(const string& _name,int _base,int sum):iWorker(_name),baseSalary(_base),saleSum(sum){} class provider:public iWorker { public: provider(const string& _name,int _base); inline string getName(); int Salary(); private: int baseSalary; }; int provider::Salary() { return baseSalary; } provider::provider(const string& _name,int _base):iWorker(_name),baseSalary(_base){} //容器与继承. //多态,就必须使用指针,而不是对象. //容器放指针的话.就会面临指针所指对象是否存在的问题. //所以如果不用堆内存,而是栈内存,也就是函数内不用new,那么就必须保证,使用容器的作用域处于创建对象的作用域的层级之下. //不能平级,更不能之上.一句话就是,使用容器的时候,容器中对象的栈,还没有弹出. //所以自己总结,安全的就是在主函数中.建立对象.主函数不到程序结束,是不可能会出栈的.即避免了new管理内存,又避免了对象的丢失. //就是不能大数据.要不栈溢出. //能不能做一个深copy的容器.这样就解决问题了啊. void showWorks(const vector<iWorker*>& workers_vecotr); class singleFactory { }; vector<iWorker*> createWorks(); int main() { //使用容器时,createWorks函数已出栈,容器内的对象已不可知,错误的使用方法.看起来好像是返回了对象.但其实返回的只是容器而已. //vector<iWorker*> workerS= createWorks(); vector<iWorker*> workerS; auto linson=sales("linson",20000,50000); workerS.push_back(&linson); auto tony=sales("tony",20000,10000); workerS.push_back(&tony); auto kely=provider("kely",20000); workerS.push_back(&kely); showWorks(workerS); return 0; } void showWorks(const vector<iWorker*>& workers_vecotr) { for(unsigned int i=0;i!=workers_vecotr.size();++i) { workers_vecotr[i]->showSalary(cout); } }

浙公网安备 33010602011771号