【C++面向对象】类继承、多态与virtual成员

一、C++类成员的访问权限

1. Public成员

public成员为类和外部“通信”的“接口”,可以在类的外部调用,没有访问限制。

2. Privatec成员

与public相对,private为类的“私有”成员,主要是供类内部调用。外部可以通过public成员(函数)来与其“通信”(获取其值或更改)。除此之外,类的“友元”(friend)可以调用类的私有成员。

3. protected成员

protected成员的访问权限处于private和public中间,在类的外部不能调用类的protected成员,只有在类的内部或者类的派生类中调用。


 

二、 C++中的继承

“多态”是面向对象程序设计的“精髓”,C++继承是实现“多态”的基础。通过继承,子类可以拥有与基类相同的类成员和功能,通过改写基类的实现可以实现与基类不同的特性。下面给出一个例子来说明类继承:

如果要设计一个书店售书的软件。书店里边有些书籍按定价出售,有些书籍要促销要打折,下面的代码中有两个类。BaseItem是普通图书,book()返回书籍的isbn,netPrice(size_t cnt)接受图书数量的值,返回总价钱。BulkItem是要打折的图书,继承自BaseItem类,BulkItem继承了book成员和price成员,可以通过book来获取其isbn,同时,BulkItem重写了基类的netPrice函数,实现与基类不同的方式计算总价,实现“多态”。

?
/*base class*/
class BaseItem
{
public :
     BaseItem(std::string &str, double p):isbn(str),price(p){}
     virtual ~BaseItem(){}
     virtual double netPrice( size_t cnt)cosnt{ return cnt*price; }
     std::string book() const { return this ->isbn; };
private :
     std::string isbn;
protected :
     double price;
}
 
/* child */
class BulkItem
{
public :
     BulkItem(std::string &str, double p):BaseItem(str,p){}
     ~BulkItem();
     double netPrice( size_t cnt) const {
     if (cnt>=minCount) return cnt*price*discount;
     else return cnt*price;}
     void setMinCount( size_t s){ minCount = s; }
     void setDiscount( double d){ discount = d; }
private :
     size_t minCount;
     double discount;
}

 


 

三、 动态绑定

在上面的例子中,在基类中netPrice函数前面有virtual标识,在子类中必须对该函数进行重新实现以实现“多态”。在实际应用中可以通过基类的引用来引用子类对象,例如:

BulkItem item(string(“isbn-2012-12-22”,20);

BaseItem &bitem = item;

bitem.netPrice(10);

在上面的代码中我们使用基类BaseItem的引用pitem引用了子类的对象item那么第三行的代码调用的是那个类的netPrice函数呢?

1. 动态绑定

当基类的引用(或指针)调用virtual函数时,会在运行时根据引用的对象(或者指针指向的对象)的类型,动态调用该类型的成员函数。

实际上,编译器会生成代码,根据运行时bitem所引用的对象的实际类型来调用相对应的函数。如果bitem所引用的为BaseItem的对象,那么调用BaseItem的成员函数,在上面的例子中,调用的是BulkItem的netPrice函数。

需要指出的是,只能是基类的“引用”或者“指针”调用virtual函数时才会动态绑定,如果使用基类的对象调用时,就肯定会调用基类的成员函数了。

BulkItem item(string(“isbn-2012-12-22”,20);

BaseItem bitem = item;

bitem.netPrice(10);

上面这段代码中,bitem调用的就是BaseItem的成员函数了。

2. 子类对象的模型

理解了子类对象的具体模型,可以很好的理解动态绑定的原理。

由于继承关系,一个子类的对象其实包含一个父类的子对象。可以这样表示:

无标题

pitem引用(或指向)的事子类对象内部的父类子对象。在运行时,如果该对象的类重新实现了virtual成员,则调用该类的成员函数。可以总结为:最中调用的成员是由“对象”本身的类型决定的,因为如果使用基类引用(或指针),只有在运行时才能确定所引用(或指向)的对象的类型。

3. virtual函数与默认实参

像其他的函数一样,虚函数也可以有默认实参,如果在调用虚函数时使用了默认实参,该实参的值是在编译时确定的。这就出现了这样一个问题:实参是在编译时确定的,而具体调用哪个实现却可能是在运行时确定的。如果使用父类的引用来引用子类对象,而该虚函数的两个实现中默认实参值不同就会出现问题。因此,虚函数在父类和子类的实现不要使用不同的默认实参。


总结:

1、 基类的析构函数加virtual后,删除基类指针时,会先调用实际object的析构函数。

2、子类在delete时,会先执行本身的析构函数再执行基类析构函数。

3、在基类member function上加virtual关键字就是为了基类指针在指向派生类对象时,实际去调用派生类对应的member function(即多态)。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值