C++ Primer 面向对象编程 15.2 基类和派生类 virtual

这篇博客探讨了C++中基类和派生类的关系,特别是关于虚函数的使用。作者强调,派生类的虚函数可以返回基类函数返回类型的派生类引用或指针,并且一旦基类函数被声明为虚函数,这个特性将不可更改。文章通过分析源代码文件(item_base.h, item_base.cpp, bulk_item.h, bulk_item.cpp, frnd.h, frnd.cpp)来加深理解。" 129566614,8753399,Python快速排序算法实现,"['Python', '算法', '数据结构', '编程语言']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写了一个小程序来复习 基类和派生类 知识

派生类中虚函数的声明(第 7.4 节)必须与基类中的定义方式完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回基类函数所返回类型的派生类的引用(或指针)。例如,Item_base 类可以定义返回 Item_base* 的虚函数,如果这样,Bulk_item 类中定义的实例可以定义为返回 Item_base*Bulk_item*

一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类无法改变该函数为虚函数这一事实。派生类重定义虚函数时,可以使用 virtual 保留字,但不是必须这样做。




1.item_base.h

#pragma once
#include <iostream>
#include <string>
using namespace std;

class item_base
{
public:
	explicit item_base( const string &isb = "base" ,  const double &pri = 1.0);

	/*
		•	继承层次的根类一般都要定义虚析构函数。
		•	除了构造函数之外,任意非 static 成员函数都可以是虚函数。
			保留字只在类内部的成员函数声明中出现,不能用在类定义体
			外部出现的函数定义上。
	*/
	virtual ~item_base(void);

	/* 友元关系不能继承 */
	friend class frnd;

public:
	virtual double net_price( size_t & ) const;
//	virtual void print_total( ostream &os , const item_base &item ,size_t n ) const;
	string book() const;
private:
	string isbn;
protected:

	/*
		•	像 private 成员一样,protected 成员不能被类的用户访问。
		•	像 public 成员一样,protected 成员可被该类的派生类访问。

	*/
	double price;
	/*
		如果基类定义 static 成员(第 12.6 节),则整个继承层次中只
		有一个这样的成员。无论从基类派生出多少个派生类,每个 static 
		成员只有一个实例。static 成员遵循常规访问控制:如果成员在基
		类中为 private,则派生类不能访问它。
	*/
	static double price_rate;

};


2.item_base.cpp

#include "item_base.h"

double item_base::price_rate = 0.0;

item_base::item_base( const string &isb,  const double & pri ):
isbn(isb) , price( pri ){
//	cout << " in base constructor." <<endl;
}

item_base::~item_base(void){
//	cout << " in base destructor." <<endl;
}

double item_base::net_price( size_t  &n) const{
	cout << " in base price." <<endl;
	return n*price;
}

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

//void item_base::print_total( ostream &os ,const item_base &item ,size_t n ) const{
//	cout << " in base print." <<endl;
//
//	os << " ISBN: " << item.book() << endl;
//	os << " total price : " <<item.net_price(n) <<endl;
//}

3.bulk_item.h

#pragma once
#include "item_base.h"

/* 
	1.	C++允许多重继承,例如 class item : public base1, base2...
	2.  声明一个包含派生列表的类(而不实现)是错误的。
		class item: public base;
	3.	函数可以设定默认默认参数,默认参数定义的顺序为自右到左。即如果一个参数设定了
		缺省值时,其右边的参数都要有缺省值

	4.	c++三种继承方式:public, private, protected 假设B类继承A类,即B类是A类的直接子类。
		public继承:A的访问属性在B类保持不变。
		A的public-----------B仍是public;        
		A的protected-------B仍是protected;
		A的private----------B无法访问(仍是private);
		protected继承:
		A的public-----------B变成protected;      
		A的protected-------B仍是protected;               
		A的private----------B无法访问(仍是private);
		private继承:
		A的public-----------B无法访问(变成private);      
		A的protected-------B无法访问(变成private);               
		A的private----------B无法访问(仍是private);
*/

class bulk_item :public item_base{		//不加public 默认是private继承
public:
	explicit bulk_item(const size_t &qty = 10 , const double &disc = 0.2/*,
		const string &isb = "derived" , const double &pri = 2.0*/);

	/*	
		派生类一般会重定义所继承的虚函数。派生类没有重定义某个虚函数,
		则使用基类中定义的版本。
	*/
	~bulk_item(void);

public:

	/*
		原则上子类重写父类虚函数时声明和定义要于父类完全一致,但有一个
		例外:虚函数返回值是父类的指针或引用 可以在子类中将返回改成子类
		的指针或引用:比如父类有虚函数:base *test(); 子类可重写成: item *test();
	*/
	/*virtual*/ double net_price( size_t & ) const;

//	virtual void print_total( ostream &os ,const item_base &item ,size_t n ) const;

	void memfcn(const bulk_item &d, const item_base &b);
private:
	size_t min_qty;
	double discount;
};

4.bulk_item.cpp

#include "bulk_item.h"

bulk_item::bulk_item(const size_t &qty  , const double &disc /*, const string
					 &isb , const double &pri*/)
:discount(disc) , min_qty(qty) /*, isbn(isb) , price(pri)*/
{
}

bulk_item::~bulk_item(void)
{
}
void bulk_item::memfcn(const bulk_item &d, const item_base &b)
{
	// attempt to use protected member
	double ret = price;		// ok: uses this->price
	ret = d.price;			// ok: uses price from a Bulk_item object

	/*	派生类只能通过派生类对象访问其基类的 protected 成员,
		派生类对其基类类型对象的 protected 成员没有特殊访问权限。*/
	//ret = b.price; // error: no access to price from an Item_base\

	/* 派生类可以访问本身protected 和基类(非对象)的protected 成员 */
	ret = bulk_item::price;	// ok: uses price from a Bulk_item class
	ret = item_base::price;	//ok:	uses price from a Bulk_item class

//	item_base::isbn;		//error C2248: “item_base::isbn”: 无法访问 private 成员
							//(在“item_base”类中声明)

	/* ok , 说明派生类也会继承基类的静态数据成员 */
	double rate1 = bulk_item::price_rate;
	double rate2 = price_rate;
}

double bulk_item::net_price( size_t &cnt ) const {
	cout << " in derived price." <<endl;
	if( cnt > min_qty )
		return cnt * ( 1- discount ) * price;
	else
		return cnt * price;
}


//void bulk_item::print_total( ostream &os ,const item_base &item ,size_t n ) const{
//	cout << " in derived print." <<endl;
//
//	os << " ISBN: " << item.book() << endl;
//	os << " total price : " <<item.net_price(n) <<endl;
//}


5.frnd.h

#pragma once
//#include "item_base.h"
#include "bulk_item.h"

class frnd
{
public:
	frnd(void);
	~frnd(void);
public:
	string visit_base_isbn( const item_base &bas) {
		return bas.isbn;
	}

	double visit_base_price( const item_base &bas){
		return bas.price;
	}

	double visit_derived_price( const bulk_item &bukl){
		return bukl.price;
	}
};


6.frnd.cpp

#include "frnd.h"

frnd::frnd(void)
{
}

frnd::~frnd(void)
{
}

7.main.cpp

#include <iostream>
#include "item_base.h"
#include "bulk_item.h"
#include "frnd.h"
using namespace std;

void print_total( ostream &os ,const item_base &item ,size_t n ) {
	//cout << " \nin base print." <<endl;
	os << " ISBN: " << item.book() << endl;

	/*	通过引用或指针调用虚函数时,编译器将生成代码,

		在运行时确定调用哪个函数,被调用的是与动态类型相对应的函数。*/
	os << " total price : " <<item.net_price(n) <<endl;
}

int main(int argc, char **argv){
	item_base base ;
	bulk_item derived;

	/*	测试动态绑定,动态绑定需要符合两个条件:调用函数必须是virtual ;必须
		要通过指针或引用调用虚函数。  动态绑定时执行函数取决于实际执行的类
		型,而不取决于指针或引用变量类型。形参为基类类型的引用	*/
	print_total( cout , base , 50 );
	print_total( cout , derived , 50);

	/*	测试指针 将基类类型的引用或指针绑定到派生类对象对基类对象
	
		没有影响,对象本身不会改变,仍为派生类对象。*/
	item_base *pbase = &base ;
	item_base &rbase = base;
	item_base *pderived = &derived;
	item_base &rderived = derived;
	size_t cnt = 20;

	/* 调用基类的net_price */
	base.net_price(cnt);	pbase->net_price(cnt);	rbase.net_price(cnt);

	/* 调用派生类的 net_price */
	derived.net_price(cnt);	pderived->net_price(cnt);	rderived.net_price(cnt);

	//double pri = item_base::price;	//error C2597: 对非静态成员“item_base::price”的非法引用
	//pri = bulk_item::price;			//error C2653: “bulk_item”: 不是类或命名空间名称


	/*	测试继承的类型(接口继承和实现继承)带来的影响:
		1.	class bulk_item :public item_base

		2.	class bulk_item :protected item_base ----error C2247: “item_base::book”不可访问,因为
			“bulk_item”使用“protected”从“item_base”继承

		3.	class bulk_item :private item_base ----error C2247: “item_base::book”不可访问,因为
			“bulk_item”使用“private”从“item_base”继承
		*/
	derived.book();

	/* 测试友元 */
	frnd fnd;
	fnd.visit_base_isbn(base);
	fnd.visit_base_price(base);
	fnd.visit_derived_price(derived);

	system("pause");
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值