当前位置: 首页 > wzjs >正文

手机网站建设多少钱seo基础培训教程

手机网站建设多少钱,seo基础培训教程,个人网站主页怎么做,wordpress文章略缩图目录 一、确保public继承塑模出is-a关系 二、避免隐藏继承而来的名称 三、区分接口继承和实现继承 四、考虑virtual函数以外的其他选择 五、不要重新定义继承而来的non-virtual函数 六、不要重新定义继承而来的缺省参数 七、尽量使用复合塑模出has-a 总结 一、确保publ…

目录

一、确保public继承塑模出is-a关系

二、避免隐藏继承而来的名称

三、区分接口继承和实现继承

四、考虑virtual函数以外的其他选择

五、不要重新定义继承而来的non-virtual函数

六、不要重新定义继承而来的缺省参数

七、尽量使用复合塑模出has-a

总结


一、确保public继承塑模出is-a关系

还是先以一个简单的例子来说明

class Person
{//…………
};class Student : public Person
{//…………
};

根据生活经验我们知道,每一个学生都是人,但是并不是每一个人是学生,这就是所谓的is-a的关系。我们可以预期,对人可以成立的每一件事对学生也是成立的,学生是一种特殊的人。

所以,任何函数如果参数是Person类型(或者是Person类型的指针或者引用),那么也可以将一个student类型传过去。

这个观点只对public继承才会成立,private继承并不属于is-a的关系。

public继承和is-a的等价关系听起来十分的简单,但是十分容易犯错误。

再举一个简单的例子:

企鹅是鸟,符合is-a的关系

鸟会飞,这也是一个事实

按照我们之前所说,企鹅是鸟,但是企鹅不会飞,如果按照上述的继承体系,我们就要好好研究一下,怎么处理企鹅会飞这一问题

有两种做法:

1、令程序在运行期间发生错误

2、在编译期间直接报错,"企鹅不会飞"

我们一般采用的是第二种做法,我们应该防止无效代码通过编译

二、避免隐藏继承而来的名称

我们还要回顾一下C++的相关概念

 所谓的隐藏是指隐藏名称,至于函数的参数和返回值并不重要

接下来再回顾一下作用域

派生类的作用域是内嵌于基类的作用域中

class Base
{
public:virtual void mf1() = 0;virtual void mf1(int);virtual void mf2();void mf3();void mf3(double);
};class Derived : public Base
{
public:virtual void mf1();void mf3();void mf4();
};

我们分别调用这些函数

Derived d;
int x;d.mf1();  // 没问题调用Derived::mf1
d.mf1(x); // 有问题Derived::mf1隐藏了Base::mf1
d.mf2();  // 没有问题调用Base::mf2
d.mf3();  // 没有问题调用Derived::mf3
d.mf3(x); // 有问题Derived::mf3隐藏了Base::mf3

 如果想要调用到Base类中的成员函数,需要我们手动指明作用域

三、区分接口继承和实现继承

身为class的设计者,有时希望派生类只继承成员函数的接口,有时又希望派生类同时继承函数的接口和实现,但是又希望能够重写它们所继承的实现,有时候又希望派生类同时继承函数的接口和实现,并且不允许重写任何东西

class Shape
{
public:virtual void draw() const = 0;virtual void error(const std::string& msg);int objectID() const;
};class Rectangle : public Shape
{};class Ellipse : public Shape
{};

我们看这个简单的继承关系

Shape是一个抽象类,因为它有纯虚函数draw,所以它不能够实例化出对象,它只能够实例化它的派生类,但是他还是强烈的影响了与它继承的所有派生类,因为它的全部派生类都继承了它的纯虚函数,如果派生类没有重写该虚函数,导致它的派生类依然是抽象类。

1、成员函数的接口总是被继承的,所谓接口就是值它的函数声明

2、纯虚函数只具体指定接口继承

3、非纯虚函数具体指定接口继承及缺省实现继承

4、普通成员函数具体指定接口继承以及强制性实现继承

但是普通成员函数同时指定函数声明和函数缺省行为,却可能造成危险

假如有一家航空公司,它现在有两种飞机A,B,两者都以相同的方式飞行

下面是简单的代码实现

class AirPlane
{
public:virtual void fly(const std::string &destination);
};void AirPlane::fly(const std::string &destination)
{std::cout << " fly "<< "青岛"<< " to " << destination << std::endl;
}class ModelA : public AirPlane
{
public:virtual void fly(const std::string &destination){AirPlane::fly(destination);}
};class ModelB : public AirPlane
{
public:virtual void fly(const std::string &destination){AirPlane::fly(destination);}
};

为了表明不同飞机的飞行方式,所以fly函数加上了virtual声明,代表继承接口和缺省实现继承

这是一个典型的面向对象的设计,两个class共享同一份fly函数,所有的飞机共性都放到了Base类中,避免了代码重复,减少长期维护的成本。

现在该公司添加了C型飞机,该公司程序员增加了class ModeC,但是忘记重新定义了C的fly函数

class ModelC : public AirPlane
{
public://…………
};

这将造成重大灾难,因为C机型飞行方式根本与前两种飞行方式完全不同

为了避免这样的问题,我们可以将fly函数声明为纯虚函数,只继承它的接口,不继承它的实现,这样因为没有重写虚函数导致C还是抽象类而报错

这里将基类改成抽象类 

class AirPlane
{
public:virtual void fly(const std::string &destination) = 0;
};

编译时就会报错,避免了无效代码编译通过

 


class ModelC : public AirPlane
{
public:virtual void fly(const std::string &destination){std::cout << "Model C "<< " fly "<< " 青岛 "<< "to" << destination << std::endl;}
};

这里我们重写C机型的fly函数,而其他机型的fly函数什么都不用做


class AirPlane
{
public:virtual void fly(const std::string &destination) = 0;
};void AirPlane::fly(const std::string &destination)
{std::cout << " fly "<< "青岛"<< " to " << destination << std::endl;
}class ModelA : public AirPlane
{
public:virtual void fly(const std::string &destination){AirPlane::fly(destination);}
};class ModelB : public AirPlane
{
public:virtual void fly(const std::string &destination){AirPlane::fly(destination);}
};class ModelC : public AirPlane
{
public:virtual void fly(const std::string &destination){std::cout << "Model C "<< " fly "<< " 青岛 "<< "to" << destination << std::endl;}
};

 

四、考虑virtual函数以外的其他选择

假如你在写一个FPS游戏,游戏中的人物有血量限制,需要为任务角色类添加一个成员函数来计算血量。因为每一个人物会有不同的方式计算血量,所以让这个成员函数声明为virtual

class Character
{
public:virtual int healthValue() const;//…………
};

但是我们还可以选择更好的实现方法

class Character
{
public:int healthValue() const{int retVal = 0;//…………retVal = doHealthValue();//…………return retVal;}
private:virtual int doHealthValue() const{//………………}
};

这种通过非virtual成员函数间接调用private virtual函数称为"non-virtual interface"(NVI)手法

它是Template Method设计模式的一种独特表现形式,这个非virtual函数称为外覆器。

外覆器能够确保在一个virtual函数被调用之前设定好适当场景,并在调用结束之后清理场景

事前工作包括锁定互斥器,记录日志等等


NVI模式下,没有必要让virtual函数一定是private的,可以灵活变通,某些class继承体系要求派生类在virtual函数的实现内必须调用其基类的兄弟,为了让这种操作合法,virtual函数必须声明为protected


另一种做法是使用function来完成Strategy模式

我们可以让每个角色的构造函数接受一个function,function中是计算血量的函数(仿函数,lambda表达式),然后我们直接调用function就能够根据不同的角色来计算血量


int defaultHealthValue(const Character& c);class Character
{typedef std::function<int(const Character&)> HealthCalcFunc;
public:Character(HealthCalcFunc hfc = defaultHealthValue):_healthFunc(hfc){}int healthValue() const{return _healthFunc(*this);}private:HealthCalcFunc _healthFunc; 
};

五、不要重新定义继承而来的non-virtual函数

class A
{
public:void func() const{std::cout << "Hello A" << std::endl;}
};class B : public A
{
};B b;
A *pa = &b;
pa->func();//调用A::funcB* pb = &b;
pb->func();//调用B::func

原因是普通成员函数是静态绑定的,pa定义为A*类型,所以通过它调用的普通成员函数永远是指向A所定义的版本

virtual成员函数是动态绑定的,如果func是virtual函数,无论是通过pa调用func还是通过pb调用func,他都会调用B::func

简单说:

先看函数类型,如果是普通成员函数那么就看指针类型,如果是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{}
};class Circle : public Shape
{
public:virtual void draw(ShapeColor color) const{}
};

这样写出的代码十分的诡异

当以对象调用draw函数时,一定要传参数

因为静态绑定下这个函数并没有从基类继承缺省值

如果以指针或者引用去调用draw,可以不指定参数

因为动态绑定下这个函数会从基类继承缺省参数

解决办法还是NVI手法

基类内的一个普通成员函数调用private virtual函数

class Shape
{
public:enum ShapeColor {RED,GREEN,BLUE};void draw(ShapeColor color = RED) const{doDraw(color);}
private:virtual void doDraw(ShapeColor color) const = 0;
};class Rectangle : public Shape
{
public:void draw(ShapeColor color = GREEN) const{doDraw(color);}
private:virtual void doDraw(ShapeColor color) const{//…………}
};

七、尽量使用复合塑模出has-a

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语白箱是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以黑箱的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。
实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。


总结


以上就是今天要讲的内容,本文仅仅回顾了C++继承和面向对象的细节

http://www.dtcms.com/wzjs/427748.html

相关文章:

  • 网站为什么要维护口碑营销的优势有哪些
  • 安徽省建设厅证件查询安全员c证seowhy培训
  • 山东胜越石化工程建设有限公司网站没广告的视频播放器app
  • 网站设计贵不贵十大免费无代码开发软件
  • h5自响应式网站模版白嫖永久服务器
  • 上海怎样建设网站网页制作的基本步骤
  • java网站开发教程百度认证官网
  • 中华人民共和国主席列表网页优化包括
  • 忻州网站建设培训竞价网
  • 泉州seo网站关键词优推广磁力搜索神器
  • 大兴高端网站建设ui设计
  • 网站的尺寸宁波seo搜索引擎优化
  • 乌鲁木齐城乡建设委员会的网站宁波最好的推广平台
  • 深圳好的外贸网站建设百度云搜索引擎入口网盘搜索神器
  • 绵阳网站网站建设南京百度关键字优化价格
  • 好网站推荐几个你知道的百度在线识图
  • 境外 色情网站seo技术培训教程
  • 自制100种少女心小物品关键词优化报价怎么样
  • 番禺本地网站友情链接交换教程
  • app界面设计图怎么做百度网站优化工具
  • 想自己做个网站苏州市网站
  • 怎么注册一个网站百度app登录
  • 网站 空间 下载网页代码
  • 网站建设 怎样找客户bt兔子磁力搜索引擎最新版
  • 哪家网站推广做的好猪八戒网接单平台
  • 东莞网站网络推广公司win7优化工具
  • 自己做网站的步骤开源cms建站系统
  • 采集网站如何收录广告公司推广平台
  • wordpress搜索400快速整站优化
  • 重庆做营销网站建设武汉seo管理