面向对象编程
--继承情况下的类作用域
引言:
在继承情况下,派生类的作用域嵌套在基类作用域中:如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义。
正是这种类作用域的层次嵌套使我们能够直接访问基类的成员,就好像这些成员是派生类成员一样:
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++