对继承的理解。

我的发帖。

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);
    }
}

 

posted @ 2016-11-14 13:30  琴鸟  阅读(470)  评论(0)    收藏  举报