C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域(一)

2014-11-24 12:52:57 · 作者: · 浏览: 3

面向对象编程

--继承情况下的类作用域



引言

在继承情况下,派生类的作用域嵌套在基类作用域中:如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义。

正是这种类作用域的层次嵌套使我们能够直接访问基类的成员,就好像这些成员是派生类成员一样:

    Bulk_item bulk;

    cout << bulk.book() << endl;

名字book的使用将这样确定[先派生->后基类]:

1)bulk是Bulk_item类对象,在Bulk_item类中查找,找不到名字book。

2)因为从Item_base派生Bulk_item,所以接着在Item_base类中查找,找到名字book,则引用成功的确定了。



一、名字查找在编译时发生

对象、引用或指针的静态类型决定了对象能够完成的行为。甚至当静态类型和动态类型可能不同的时候,就像使用基类类型的引用或指针时可能会发生的,静态类型仍然决定着可以使用什么成员:

class Disc_item : public Item_base
{
public:
    std::pair
  
    discount_policy() const
    {
        return std::make_pair(quantity,discount);
    }

    //other member as before...
};

  

只能通过Disc_item类型或Disc_item派生类型的对象、指针或引用访问discount_policy():

    Bulk_item bulk;
    Bulk_item *bulkP = &bulk;
    Item_base *itemP = &bulk;

    bulkP -> discount_policy(); //OK
    itemP -> discount_policy(); //Error

通过itemP访问是错误的,因为基类类型的指针(引用或对象)只能访问对象的基类部分,而不能访问派生类部分,而在基类中又没有定义discount_policy()成员。


//P498 习题15.21/22
class Item_base
{
public:
    Item_base(const std::string &book = "",
              double sales_price = 0.0):
        isbn(book),price(sales_price) {}

    std::string book() const
    {
        return isbn;
    }

    //只是返回总价格,不进行打折
    virtual double net_price(std::size_t n) const
    {
        return n * price;
    }

    virtual ~Item_base() {}

private:
    std::string isbn;

protected:
    double price;
};

class Disc_item : public Item_base
{
public:
    Disc_item(const std::string &book = "",
              double sales_price = 0.0,
              std::size_t qty = 0,
              double disc_rate = 0.0):
        Item_base(book,sales_price),quantity(qty),discount(disc_rate) {}

    //将函数设置为纯虚函数,以防止用户创建Disc_item对象
    double net_price(size_t) const = 0;

    std::pair
  
    discount_policy() const
    {
        return std::make_pair(quantity,discount);
    }

protected:
    std::size_t quantity;   //可实行折扣的数量
    double discount;        //折扣率
};

//批量购买折扣类
class Bulk_item : public Disc_item
{
public:
    Bulk_item(const std::string &book = "",
              double sales_price = 0.0,
              std::size_t qty = 0,
              double disc_rate = 0.0):
        Disc_item(book,sales_price,qty,disc_rate) {}

    double net_price(std::size_t cnt) const
    {
        if (cnt >= quantity)
        {
            return cnt * (1 - discount) * price;
        }
        else
        {
            return cnt * price;
        }
    }
};

//有限折扣类
class Lds_item : public Disc_item
{
public:
    Lds_item(const std::string &book = "",
             double sales_price = 0.0,
             std::size_t qty = 0,
             double disc_rate = 0.0):
        Disc_item(book,sales_price,qty,disc_rate) {}

    double net_price(std::size_t cnt) const
    {
        if (cnt <= quantity)
        {
            return cnt * (1 - discount) * price;
        }
        else
        {
            return price * (cnt - quantity * discount);
        }
    }
};

  

二、名字冲突与继承

与基类成员同名的派生类成员将屏蔽对基类成员的直接访问:

class Base
{
public:
    Base():mem(0){}

protected:
    int mem;
};

class Derived : public Base
{
public:
    Derived(int i):mem(i){}
    int get_mem() const
    {
        return mem; //Derived::mem
    }

private:
    int mem;    //将会屏蔽Base::mem
};

get_mem中对mem的引用被确定为Derive中的名字:

    Derived d(43);
    cout << d.get_mem() << endl;    //output 43

可以使用作用域操作符访问被屏蔽的成员:

class Derived : public Base
{
public:
    int get_mem() const
    {
        return Base::mem; //Derived::mem
    }
    //As before
};

//测试
    Derived d(43);
    cout << d.get_mem() << endl;    //output 0

作用域操作符指示编译器在Base中查找mem成员。

【最佳实践】

设计派生类时,只要可能,最好避免与基类成员的名字冲突

//P499 习题15.23
class Base
{
public:
    void foo(int);

protected:
    int bar;
    double foo_bar;
};

class Derived : public Base
{
public:
    void foo(string);
    bool bar(Base *pb);
    void foobar();

protected:
    string bar;
};

void Derived::foobar()
{
    bar = "1024";
}

bool Derived::bar(Derived *pb)
{
    return foo_bar == pb -> foo_bar;
}

int main()
{
    Derived d;
    d.foo("1024");
}
/*说明:可能是g++编译器对类型检查比较严格,这个程序在g++