您的位置:首页 > 编程语言 > C语言/C++

C++ Primer 面向对象编程 15.5 动态绑定和静态绑定 类作用域

2016-06-05 17:33 555 查看

一.动态绑定和静态绑定

为了支持c++的多态性,才用了动态绑定和静态绑定。理解他们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误,需要理解四个名词: 

1、对象的静态类型:对象在声明时采用的类型。是在编译期确定的。 

2、对象的动态类型:目前所指对象的类型。是在运行期决定的

3、静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。

4、动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。

item_base.h

#pragma once
#include <iostream>
#include <string>
using namespace std;

class item_base
{
public:
explicit item_base( const string &isb = "base" , const double &pri = 1.0);

virtual ~item_base(void);

public:
virtual double net_price( size_t & ) const;

string book() const;

private:
string isbn;
protected:

double price;
static double price_rate;

};


bulk_item.h
#pragma once
#include "item_base.h"

class bulk_item :public item_base{
public:
explicit bulk_item(const size_t &qty = 10 , const double &disc = 0.2,
const string &isb = "derived" , const double &pri = 2.0);

~bulk_item(void);

public:

/*virtual*/ double net_price( size_t & ) const;

/*
在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类

作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽
*/
string book() const;

/* 派生类独有函数 , 基类没有*/
double disc_total( size_t &) const;

private:
size_t min_qty;
double discount;
};


main.cpp
#include <iostream>
#include "item_base.h"
#include "bulk_item.h"
using namespace std;

int main(int argc, char **argv){
item_base base ;
bulk_item derived;

/* 说明对象、引用或指针的静态类型和动态类型。*/
item_base *pbase = &base ; // pbase的静态类型是它声明的类型item_base *,动态类型也是item_base*
item_base &rbase = base; // rbase的静态类型是它声明的类型item_base &,动态类型也是item_base&

item_base *pderived = &derived; // pderived的静态类型是它声明的类型item_base *,动态类型是它所指向的对象的类型bulk_item*
item_base &rderived = derived; // rderived的静态类型是它声明的类型item_base &,动态类型是它所指向的对象的bulk_item&

size_t cnt = 20;

/* 测试动态绑定 , 基类和派生类都有net_price函数, 虚函数 */
pbase->net_price(cnt); //调用基类函数net_price
rbase.net_price(cnt); //调用基类函数net_price
pderived->net_price(cnt); //调用派生类函数net_price
rderived.net_price(cnt); //调用派生类函数net_price

/* 测试静态绑定 ,基类和派生类都有book函数, 非虚函数 */
pbase->book(); //调用基类函数book
rbase.book(); //调用基类函数book
pderived->book(); //调用基类函数book
rderived.book(); //调用基类函数book

system("pause");
return 0;
}

二 . 继承情况下的类作用域

在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽:
     struct Base {
         intmemfcn();
     };
     struct Derived: Base {
         intmemfcn(int); // hides memfcn in the base
     };
     Derived d;Base b;
    b.memfcn();        // calls Base::memfcn
    d.memfcn(10);      // calls Derived::memfcn
    d.memfcn();        // error: memfcnwith no arguments is hidden
    d.Base::memfcn();  // ok: calls Base::memfcn
现在可以理解虚函数为什么必须在基类和派生类中拥有同一原型了。如果基类成员与派生类成员接受的实参不同,就没有办法通过基类类型的引用或指针调用派生类函数。考虑如下(人为的)为集合:
     class Base {
     public:
         virtualint fcn();
     };
     class D1 :public Base {
     public:
          // hides fcnin the base; this fcn is not virtual
          intfcn(int); // parameter list differs from fcn in Base
          // D1 inheritsdefinition of Base::fcn()
     };
     class D2 :public D1 {
     public:
         intfcn(int); // nonvirtual function hides D1::fcn(int)
         intfcn();    // redefines virtual fcn from Base
     };
D1 中的 fcn 版本没有重定义 Base 的虚函数 fcn,相反,它屏蔽了基类的 fcn。结果 D1 有两个名为 fcn 的函数:类从 Base 继承了一个名为 fcn 的虚函数,类又定义了自己的名为 fcn 的非虚成员函数,该函数接受一个int 形参。但是,从 Base 继承的虚函数不能通过 D1 对象(或 D1 的引用或指针)调用,因为该函数被 fcn(int) 的定义屏蔽了。
The class D2 redefines both functions that it inherits. It redefinesthe virtual version of fcn originally defined in Base and the nonvirtual defined in D1.
类 D2 重定义了它继承的两个函数,它重定义了 Base 中定义的 fcn 的原始版本并重定义了 D1 中定义的非虚版本。
※通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类:

     Base bobj;  D1 d1obj;  D2 d2obj;

     Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;

     bp1->fcn();   // ok: virtual call, will call Base::fcnat run time

     bp2->fcn();   // ok: virtual call, will call Base::fcnat run time

     bp3->fcn();   // ok: virtual call, will call D2::fcnat run time


15.6 纯虚函数

    纯虚函数申明很简单 void show()=0;拥有纯虚函数的类无法定义对象,但可以定义指针或引用。假设基类 base 定义了纯虚函数。

    base c ;  // 错误

    base *c = &b ;  // 正确

    base &c = b ;  // 正确

15.7 容器与继承

    容器对象可以定义成存放基类对象,但可以给容器加入子类对象,这时候子类会被转换成基类对象,或者说基类部分会被系统删除。

    可以定义基类指针或引用类型容器,再增加子类指针活引用,这时候会更具实际内容不同执行不同代码(动态绑定)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ primer class