`
java-mans
  • 浏览: 11415232 次
文章分类
社区版块
存档分类
最新评论

虚函数与动态绑定

 
阅读更多

在定义基类时,我们希望基类中的有些函数可以在派生类中重新定义。比如,我们定义了基类记录的书,可以求出买了多少书花了多少钱;而在派生类中,我们定义的是打折的书,还是要计算买了多少书花了多少钱。这时,就需要重新定义计算钱数的函数了。注意,这里的重新定义,与之前讲过的函数重载或者操作符重载不同:后面两类,是通过不同的形参,返回值类型来让编译器判断到底使用的是哪个函数,在程序执行前就能判断;而这里的重新定义,只是函数体的内容不同,参数、返回值都与积累完全相同。只有程序运行时才能判断使用的到底是哪个层次中的函数(这个过程成为动态绑定),这样做是为了让程序的接口统一。
通过把一个函数声明为虚函数,(这个函数不能使这个类的构造函数,也不能是static函数)我们就可以在派生类中重新定义它。虚函数在声明时,需要加上关键字virtual,不能在类的外部出现这个关键字。这一点和友元的声明不同:友元可以既可以在类内不声明,也可以在类外部声明。当一个函数被声明为虚函数后,这个函数在对于基类的后代而言,就都是虚函数了,不用加上virtual关键字。(当然,你也可以加上以强调它是虚函数)。

只有通过基类的引用或者指针调用虚函数时,才能发生动态绑定。因为引用或者指针既可以指向基类对象,也可以指向派生类对象的基类部分,用引用或者指针调用的虚函数。
举一个例子:

class Item_base
{
public:
	//构造函数
	Item_base(const std::string &book = "",double sales_price = 0.0):
		isbn(book),price(sales_price){ }
	//返回isbn号
	std::string book(){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 Bulk_item:public Item_base
{
public:
	//构造函数
	Bulk_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){ }
	~Bulk_item(){}
	double net_price(std::size_t)const;


private:
	//买多少书以后才有折扣
	std::size_t quantity;
	//折扣幅度
	double discount;
};


其中:

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

}

我们再定义一个打印结果的函数:
我们再定义一个打印结果的函数:我们再定义一个打印结果的函数:我们再定义一个打印结果的函数:我们再定义一个打印结果的函数:

void print_total(std::ostream& os,const Item_base& item,std::size_t n)
{
	os<<"ISBN: "<<item.book()<<"\tnumber sold: "<<n<<"\ttotal price: "<<item.net_price(n)<<std::endl;
}

那么在主函数中:

int main()
{

	Item_base b1("aaa",10);
	Bulk_item d1("bbb",10,5,0.5);
	print_total(cout,b1,10);
	print_total(cout,d1,10);
	return 0;
}

可以发现,虽然print_total函数的输入参数是基类的引用,我们能够也能够通过派生类来访问它。当使用基类访问它时,调用的是基类的net_price函数,派生类访问它时,调用的是派生类的net_price函数。这个调用规则不是在编译时可以确定的,必须在程序运行时才能确定。所以称之为“动态关联”。与之相对的,是静态关联,比如函数的重载,操作符的重载等等。程序执行之前,就能确定调用的是哪个函数。

特别的,有时我们会遇到这种情况:我们定义了一个类,这个类也有自己的数据和函数,但是我们并不希望用户建立这个类的对象,而只能建立从这个基类派生出的类的对象。有人觉得,既然我们不希望建立一个一个类的对象,那么我们干嘛要定义这个类呢?其实很多时候,基类,是对一些问题的抽象,对它建立对象是没有意义的。我们只是用它派生其他的,具体的类。比如,我们建立了“动物”类,定义了动物的“身高”、“体重”,定义了动物的动作:“吃饭”、“睡觉”。从“动物”类派生出“老虎”等类。此时,大家也应该觉得,定义一个动物类对象没有多大的意义。
言归正传,如何实现上面的构想呢?就是使用纯虚函数。纯虚函数的定义很简单,就是在某个函数的形参列表后面加上=0。但这一举动却意义非凡:首先我们定义了这个函数(而不是在派生类中定义),是得我们为这类函数提供了统一的、可覆盖的接口,以便于后面的管理;其次,当一个类中含有(或者)纯虚函数时,这个类就被成为“抽象基类”,我们不能创建这个类的对象,只能用它来派生别的类,创建派生类的对象,这样规定,也防止我们误用了这个类(因为他是被用来继承的,很多成员都不完善。)举一个例:

class annimal
{
public:
	virtual void eat()=0;
	void sleep(){};

private:
	double height;
	double weight;
};

class tiger:public annimal
{
public:
	void eat(){std::cout<<"eat meat"<<std::endl;}
	void sell(){std::cout<<"sleep at night"<<std::endl;};
};

class bat:public annimal
{
public:
	void eat(){std::cout<<"eat blood"<<std::endl;};
	void sell(){std::cout<<"sleep at day"<<std::endl;};

};
int main()
{
	//不能创建抽象类的对象
	//annimal a1;
	tiger t1;
	t1.eat();
	bat b1;
	b1.eat();
	return 0;
}



分享到:
评论

相关推荐

    c++ 对比虚函数的动态绑定

    c++ 对比虚函数的c++ 对比虚函数的动态绑定动态绑定

    虚函数的原理,如果能够了解C++编译器对于虚函数的实现方式,我们就能够知道为什么虚函数可以做到动态绑定

    你一定很想知道虚函数是怎样做出来的,对...如果能够了解C++编译器对于虚函数的实现方式,我们就能够知道为什么虚函数可以做到动态绑定。本文就精要的阐述了虚函数的精髓,相信会给你一种恍然大悟,焕然一新的感觉。

    cpp代码-基类调用虚函数的动态绑定

    cpp代码-基类调用虚函数的动态绑定

    C++多态性与虚函数

    第9章 多态性与虚函数 本章学习要求: 虚函数 多态性 静态与动态联编 9.1 多态性的概念 9.2 虚函数 9.3 静态绑定与动态绑定 9.4 纯虚函数和抽象类

    虚函数的原理,虚函数实现方式

    虚函数的原理,如果能够了解C++编译器对于虚函数的实现方式,我们就能够知道为什么虚函数可以做到动态绑定虚函数的原理,如果能够了解C++编译器对于虚函数的实现方式,我们就能够知道为什么虚函数可以做到动态绑定

    吕鑫:【C++语法与数据结构第22天】【第3堂课】动态绑定与静态绑定(面试题)

    1、讲解和演示动态绑定与静态绑定的概念与原理; 2、讲解和演示虚析构函数的概念与原理,并讲解为什么构造函数不能使用虚函数的原因;

    C++中虚函数的实现机制

    介绍了C++编程语言中的虚函数及其在进行面向对象程序设计中重要性,并且详细阐述了它在...它通过一个vptr和vtable在运行时进行动态绑定,从而能够根据对象类型的不同调用不同的 虚函数;并通过实例测试验证了上述机制.

    详解C++虚函数的工作原理

    静态绑定与动态绑定 讨论静态绑定与动态绑定,首先需要理解的是绑定,何为绑定?函数调用与函数本身的关联,以及成员访问与变量内存地址间的关系,称为绑定。 理解了绑定后再理解静态与动态。 静态绑定:指在程序...

    C++中虚函数与纯虚函数的用法

    本文较为深入的分析了C++中虚函数与纯虚函数的用法,对于学习和掌握面向对象程序设计来说是至关重要的。具体内容如下: 首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承、动态...

    C++中的静态绑定和动态绑定

    C++在面向对象编程中,存在着静态绑定和动态绑定的定义,本节即是主要讲述这两点区分。  我是在一个类的继承体系中分析的,因此下面...  从上面的定义也可以看出,非虚函数一般都是静态绑定,而虚函数都是动态绑定

    C++对象内存分布详解(包括字节对齐和虚函数表)

    C++对象的内存分布和虚函数表注意,对象中保存的是虚函数表指针,而不是虚函数表,虚函数表在编译阶段就已经生成,同类的不同对象中的虚函数指针指向同一个虚函数表,不同类对象的虚函数指针指向不同虚函数表。...

    浅谈C++中虚函数实现原理揭秘

    编译器到底做了什么实现的虚函数的晚绑定呢?我们来探个究竟。  编译器对每个包含虚函数的类创建一个表(称为V TA B L E)。在V TA B L E中,编译器放置特定类的虚函数地址。在每个带有虚函数的类 中,编译器秘密地...

    深入理解C++的动态绑定与静态绑定的应用详解

    为了支持c++的多态性,才用了动态绑定和静态绑定。理解他们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误。需要理解四个名词:1、对象的静态类型:对象在声明时采用的类型。是在编译期确定的。2、...

    关于vptr(虚指针)和vtbl(虚函数表)1

    2.静态绑定与动态绑定在C中,对于不同的函数名采用静态绑定的方法,每个函数直接对应了一个地址,存储在相应的位置中。需要注意的是,new和delete操作应该是静

    C++多态的实现及原理详细解析

    4. 多态用虚函数来实现,结合动态绑定。5. 纯虚函数是虚函数再加上= 0。6. 抽象类是指包括至少一个纯虚函数的类。 纯虚函数:virtual void breathe()=0;即抽象类!必须在子类实现这个函数!即先有名称,没内容,在...

    C++语言中的虚函数研究-综合文档

    C++语言中的虚函数研究虚函数是面向对象的C++语言中的一个非常重要的概念.它充分体 现了面向对象思想中的继承和多态性这两大特性,动态绑定是C++中实现多态的一个重要途径,虚函数是动态绑定

    详解C++纯虚函数与抽象类

    1.虚函数 1.1虚函数简介 虚函数可以毫不夸张的说是C++最重要的特性之一,我们先来看一... 如果定义为虚函数,那么它就是动态绑定的,也就是在派生类中可以被覆盖的,这与静态成员函数的定义(在内存中只有一份拷贝,

    C++ 基础教程之虚函数实例代码详解

    虚函数:就是在编译的时候不确定要调用哪个函数,而是动态决定将要调用哪个函数。它的作用就是为了能让这个函数在它的子类里面可以被重载,这样的话,编译器就可以使用后期绑定来达到多态了,也就是用基类的指针来...

Global site tag (gtag.js) - Google Analytics