封装:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项,或者叫接口。(数据安全机制)
有了封装,就可以明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者;而外部调用者也可以知道自己不可以碰哪里。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
继承+多态:继承和多态必须一起说。一旦割裂,就说明理解上已经误入歧途了。
先说继承:继承同时具有两种含义:其一是继承基类的方法,并做出自己的改变和/或扩展——号称解决了代码重用问题;其二是声明某个子类兼容于某基类(或者说,接口上完全兼容于基类),外部调用者可无需关注其差别(内部机制会自动把请求派发[dispatch]到合适的逻辑)。(继承就是一种特殊的代码复用功能)
再说多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。(简单的理解 多态 == 覆盖和调用虚函数 )(多态比较复杂,各种骚操作都要用多态实现)
很显然,多态实际上是依附于继承的两种含义的:“改变”和“扩展”本身就意味着必须有机制去自动选用你改变/扩展过的版本,故无多态,则两种含义就不可能实现。
所以,多态实质上是继承的实现细节;那么让多态与封装、继承这两个概念并列,显然是不符合逻辑的。不假思索的就把它们当作可并列概念使用的人,显然是从一开始就被误导了——正是这种误导,使得大多数人把注意力过多集中在多态这个战术层面的问题上,甚至达到近乎恶意利用的程度;同时却忽略了战略层面的问题,这就致使软件很容易被他们设计成一滩稀屎(后面会详细谈论这个)。
class Fish // base class
{
// ... Fish's members
};
class Carp:public Fish
// derived class
{
// ... Carp's members
};
鱼类世界呈现的一种简单的继承层次结构
#include <iostream>
#include <string>
using namespace std;
class Fish
{
public:
bool is_fresh_water_fish;
void swim()
{
if (is_fresh_water_fish)
{
cout << "Swims in lake" << endl;
}
else
{
cout << "Swims in sea" << endl;
}
}
};
class Tuna: public Fish
{
public:
Tuna(){
is_fresh_water_fish = false;
}
};
class Carp: public Fish
{
public:
Carp()
{
is_fresh_water_fish = true;
}
};
int main(){
Carp my_lunch;
Tuna my_dinner;
cout << "About my food: " << endl;
cout << "Lunch: ";
my_lunch.swim();
cout << "Dinner: ";
my_dinner.swim();
return 0;
}
基类初始化—向基类传递参数
如果基类包含重载的构造函数,需要在实例化时给它提供实参,该如何办呢?创建派生对象时将 如何实例化这样的基类?方法是使用初始化列表,并通过派生类的构造函数调用合适的基类构造函数
class Base
{
public: Base(int someNumber) // overloaded constructor
{
// Use someNumber
}
};
Class Derived: public Base {
public: Derived(): Base(25) // instantiate Base with argument 25
{
// derived class constructor code
}
};
对 Fish 类来说,这种机制很有用。通过给 Fish 的构造函数提供一个布尔输入参数,以初始化 Fish::isFreshWaterFish,可强制每个派生类都指出它是淡水鱼还是海水鱼
#include <iostream>
#include <string>
using namespace std;
class Fish
{
protected:
bool is_fresh_water_fish;
public:
Fish(bool is_fresh_water): is_fresh_water_fish(is_fresh_water){}
void swim()
{
if (is_fresh_water_fish){
cout << "Swims in lake" << endl;
}
else{
cout << "Swims in sea" << endl;
}
}
};
class Tuna: public Fish
{
public:
Tuna():Fish(false) {}
};
class Carp: public Fish
{
public:
Carp():Fish(true){}
};
int main(){
Carp my_lunch;
Tuna my_dinner;
cout << "About my food: " << endl;
cout << "Lunch: ";
my_lunch.swim();
cout << "Dinner: ";
my_dinner.swim();
return 0;
}
在派生类中覆盖基类的方法
如果派生类实现了从基类继承的函数,且返回值和特征标相同,就相当于覆盖了基类的这个方法, 如下面的代码所示:
class Base
{
public:
void DoSomething()
{
// implementation code… Does something
}
};
class Derived:public Base
{
public:
void DoSomething()
{
// implementation code… Does something else
}
};
因此,如果使用 Derived 类的实例调用方法 DoSomething( ),调用的将不是 Base 类中的这个方法。如果 Tuna 和 Carp 类实现了自己的 Swim( )方法,则在程序清单 10.3 的 main( )中下述代码将调用Tuna::Swim( )的实现,这相当于覆盖了基类 Fish 的方法 Swim( ):
在派生类中调用基类的方法
通常,Fish::Swim( )包含适用于所有鱼类(包括金枪鱼和鲤鱼)的通用实现。如果要在 Tuna::Swim( ) 和 Carp::Swim( )的实现中重用 Fish::Swim( )的通用实现,可使用作用域解析运算符(::),如下面的代码所示:
class Carp: public Fish
{
public:
Carp(): Fish(true) {}
void Swim()
{
cout << "Carp swims real slow" << endl;
Fish::Swim(); // invoke base class function using operator::
}
};
在基类方法和 main( )中,使用作用域解析运算符(::)来调用基类方法
#include <iostream>
#include <string>
using namespace std;
class Fish
{
private:
bool is_fresh_water_fish;
public:
Fish(bool is_fresh_water): is_fresh_water_fish(is_fresh_water){}
void swim()
{
if (is_fresh_water_fish){
cout << "Swims in lake" << endl;
}
else{
cout << "Swims in sea" << endl;
}
}
};
class Tuna: public Fish
{
public:
Tuna():Fish(false) {}
void swim()
{
cout << "Tuna swims real fast, ";
}
};
class Carp: public Fish
{
public:
Carp():Fish(true){}
void swim()
{
cout << "Carp swims real slow, ";
Fish::swim();
}
};
int main(){
Carp my_lunch;
Tuna my_dinner;
cout << "About my food: " << endl;
cout << "Lunch: ";
my_lunch.swim();
cout << "Dinner: ";
my_dinner.swim();
my_dinner.Fish::swim();
return 0;
}