c++ 多态介绍

C++ 多态介绍

用c++ 虚函数机制实现的多态是继数据抽象(封装)和继承之后第三个重要的面向对象的特性

向上转型(Upcast problem)

//: C15:Instrument2.cpp
// Inheritance & upcasting
#include 
using namespace std;
enum note { middleC, Csharp, Eflat }; // Etc.

class Instrument {
public:
  ///use virtual function can get excepted outcome
  void play(note) const {
    cout << "Instrument::play" << endl;
  }
};

// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {
public:
  // Redefine interface function:
  void play(note) const {
    cout << "Wind::play" << endl;
  }
};

///pass by reference
void tune(Instrument& i) {
  // ...
  i.play(middleC);
}

int main() {
  Wind flute;
  tune(flute); // Upcasting
} ///:~

tune 函数接受一个Instrument类型的引用参数,但是没有对于传入的Wind类型的参数报错。
上述代码并没有得到预期的结果,输出为 “Instrument::play” 若要得到预期结果“Wind::play”,则需要将基类的play函数声明为virual.

函数调用绑定(Function call binding)

连接函数调用到函数首地址成为绑定 ( Connecting a function call to a function body is called binding. )

当绑定发生在程序运行之前,由编译器和链接器执行的绑定为早期绑定 ( When binding is performed before the program is run (by the compiler and linker), it’s called early binding. )

early binding 成为早绑定,或者早期联编

上述问题发生的原因在于早期绑定,因为当tune函数只有一个Instrument地址时,编译器不知道正确的函数调用。 ( The problem in the program above is caused by early binding because the compiler cannot know the correct function to call when it has only an Instrument address.

解决办法称为迟绑定,这种绑定发生在运行期间,以对象的类型为基础。迟绑定也称为动态绑定或(执行绑定) ( The solution is called late binding, which means the binding occurs at runtime, based on the type of the object. Late binding is also called dynamic binding or runtime binding.

当一个语言实现了迟绑定,就必须有一种机制决定对象在运行期间的类型并且调用合适的方法。
( When a language implements late binding, there must be some mechanism to determine the type of the object at runtime and call the appropriate member function. )

在某种编写的语言下,编译器无法知道实际的对象类型,但是它可以通过内嵌代码找到并调用合适的函数体
( In the case of a compiled language, the compiler still doesn’t know the actual object type, but it inserts code that finds out and calls the correct function body. )

这种迟绑定机制在不同的程序语言之间有差异,但是可以想象运用某些信息关于类型的排序到对象上。
( The late-binding mechanism varies from language to language, but you can imagine that some sort of type information must be installed in the objects. You’ll see how this works later. )

虚函数(virtual functions)

为了实现动态绑定需要一个特定的函数,C++要求使用virtual关键字在声明基类的函数时
( To cause late binding to occur for a particular function, C++ requires that you use the virtual keyword when declaring the function in the base class. )

迟绑定仅仅通过虚函数实现,并且尽管虚函数被定义在一个更早的基类中,当使用基类作为地址时当前基类的虚函数也是存在的
( Late binding occurs only with virtual functions, and only when you’re using an address of the base class where those virtual functions exist, although they may also be defined in an earlier base class. )

当基类中的函数被定义成虚的时,该函数在所有的派生类中都是虚的
( If a function is declared as virtual in the base class, it is virtual in all the derived classes.)

在派生类中重新定义一个虚函数,也叫做重写
(The redefinition of a virtual function in a derived class is usually called overriding.)

注意:只需要在基类中定义虚函数,所有的派生类中的可以匹配到基类虚函数的声明的函数可以通过虚函数机制调用。可以在派生类中使用virtual,但是这事多余的并且可能引起迷惑
( Notice that you are only required to declare a function virtual in the base class. All derived-class functions that match the signature of the base-class declaration will be called using the virtual mechanism. You can use the virtual keyword in the derived-class declarations (it does no harm to do so), but it is redundant and can be confusing.)

程序的可扩展性(Extensibility)

一个具有良好面向对象设计的程序,大部分函数都会向tune这样定义,通过基类作为接口
( In a well-designed OOP program, most or all of your functions will follow the model of tune( ) and communicate only with the base-class interface. )

//
// Created by 王若璇 on 17/3/26.
//

// Extensibility in OOP
#include 
using namespace std;
enum note { middleC, Csharp, Cflat }; // Etc.

class Instrument {
public:
    virtual void play(note) const {
        cout << "Instrument::play" << endl;
    }
    virtual char* what() const {
        return "Instrument";
    }
    // Assume this will modify the object:
    virtual void adjust(int) {}
};

class Wind : public Instrument {
public:
    void play(note) const {
        cout << "Wind::play" << endl;
    }
    char* what() const { return "Wind"; }
    void adjust(int) {}
};

class Percussion : public Instrument {
public:
    void play(note) const {
        cout << "Percussion::play" << endl;
    }
    char* what() const { return "Percussion"; }
    void adjust(int) {}
};

class Stringed : public Instrument {
public:
    void play(note) const {
        cout << "Stringed::play" << endl;
    }
    char* what() const { return "Stringed"; }
    void adjust(int) {}
};

class Brass : public Wind {
public:
    void play(note) const {
        cout << "Brass::play" << endl;
    }
    char* what() const { return "Brass"; }
};

class Woodwind : public Wind {
public:
    void play(note) const {
        cout << "Woodwind::play" << endl;
    }
    char* what() const { return "Woodwind"; }
};

// Identical function from before:
void tune(Instrument& i) {
    // ...
    i.play(middleC);
}

// New function:
void f(Instrument& i) { i.adjust(1); }

// Upcasting during array initialization:
Instrument* A[] = {
        new Wind,
        new Percussion,
        new Stringed,
        new Brass,
};

int main() {
    Wind flute;
    Percussion drum;
    Stringed violin;
    Brass flugelhorn;
    Woodwind recorder;
    tune(flute);
    tune(drum);
    tune(violin);
    tune(flugelhorn);
    tune(recorder);
    for(int i = 0;i<4;i++){
        tune(*A[i]);
    }
    f(flugelhorn);
} ///:~

又一个新的继承关系被添加到Wind的下面,无论有多少继承层次虚机制仍然正确工作
( You can see that another inheritance level has been added beneath Wind, but the virtual mechanism works correctly no matter how many levels there are. )

在Brass和Woodwind类中没有重写adjust方法,当发生函数调用时,在继承关系集团中的最近定义的函数将会被自动的使用。编译器保证总是有一些对于虚函数的定义,所以永远不要调用一个没有绑定函数体的函数,那将是一场灾难。
( The adjust( ) function is not overridden for Brass and Woodwind. When this happens, the “closest” definition in the inheritance hierarchy is automatically used – the compiler guarantees there’s always some definition for a virtual function, so you’ll never end up with a call that doesn’t bind to a function body. (That would be disastrous.) )

数组A[]包含了指向基类的指针,因此向上转型发生在数组初始化的过程中
( The array A[ ] contains pointers to the base class Instrument, so upcasting occurs during the process of array initialization. )

在调用函数tune()的过程中,尽管每一个不同的对象都发生了向上转型,但是我们得到了期望的结果
( In the call to tune( ), upcasting is performed on each different type of object, yet the desired behavior always takes place. )

当你尝试分析一个项目时,虚函数是一个镜片。它可以解决基类在什么时候什么地方声明,如何去扩展项目等问题
(The virtual function is the lens to use when you’re trying to analyze a project: Where should the base classes occur, and how might you want to extend the program?)

c++类的模块化高内聚的特点,使得当程序员改变了系统的一部分导致一个大的问题发生时,与c的处理方式不同,不会影响与传播到系统的其它部分
( Because of the tight class modularization in C++, it isn’t a large problem when this occurs because changes you make in one part of a system tend not to propagate to other parts of the system as they do in C. )

Reference

Thinking in c++ 15

你可能感兴趣的:(C++进阶学习笔记)