【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函数与默认实参

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


2012-12-23  01:53:49

你可能感兴趣的:(virtual)