程序设计二(面向对象)_实训13_虚函数实验答案补充

很多粉丝朋友留言表示在程序设计二(面向对象)_实训13_虚函数实验的第一关:设计和使用虚函数 的答案当中,缺少了Linked List部分的答案,现在予以补充:

第1关:设计和使用虚函数

任务描述

建立一个继承体系。List 是基类,ArrayListLinkedList 是派生类。

List 提供 5 个函数,分别是增删查改显。其中,前 4 个是纯虚函数,第 5 个是虚函数。

用户需在 ArrayListLinkedList 中编写实现相应的实现。

注意一条:在 ArrayList 中无需再实现显示函数。

相关知识

虚函数是 C++ 实现动态绑定的关键。所谓动态绑定,如下:

 
  1. List *pList;
  2. pList = new ArrayList;
  3. pList->insert(pList->getSize()-1,someValue);//ArrayList做尾插入比较快
  4. delete pList;
  5. pList = new LinkedList;
  6. pList->insert(0,someValue);//LinkedList做头插入比较快
  7. delete pList;

从语法上看,pList 只是一个指向 List 类型的指针。但是,在第 2、3 行代码中,pList 实际上指向了一个 ArrayList 对象。此时通过 pList 调用了 insert 成员函数。那么,此时调用的是 List 类的insert函数还是 ArrayList 类的 insert 函数呢?这取决于是否为虚函数。

如果 insert 成员函数是虚函数,则此时调用的是 ArrayList 的成员函数。如果 insert 成员函数不是虚函数,则此时调用的是 List 的成员函数。

因此,如果将insert函数设置为虚函数,就可以实现这样一种效果。如同上述代码的第 5、6 行所写。通过 pList 指针,既可以调用 ArrayListinsert 函数,也可以调用 LinkedListinsert 函数。

这样做有什么好处呢?我们来描述这样一种情形。需求是开发一款使用工业摄像头的图像处理程序。于是购买了 A 公司的摄像头产品,并使用其提供的图形采集函数,编程的代码如下:

 
  1. A a;//A公司的摄像头对象
  2. a.initialize();//初始化
  3. while(1){ //死循环
  4. a.grab();//采集一帧
  5. /*这里作图像处理*/
  6. }

过了一段时间,因为各种原因,要改用 B 公司的摄像头,当然随B公司也将随产品提供其自身的函数。于是,我们需要修改代码:

 
  1. B a;//B公司的摄像头对象
  2. a.start();//初始化,为什么B公司的函数名不做成跟A公司一样呢?
  3. while(1){//死循环
  4. a.process();//采集一帧,这里不对B公司的取名抱任何期望
  5. /*这里作图像处理*/
  6. }

你会发现,如果代码中涉及到的具体的摄像头函数越多,当你改用别家产品的时候,你需要修改的代码就越多。这意味着需要花费更多的人力、物力、时间,而且更容易出错。 可以这样做,仍然是先用 A 公司的,但是设计一个 Camera 基类,然后再派生一个类用于表示 A 公司的摄像头:

 
  1. class Camera{
  2. public:
  3. virtual void init();
  4. virtual void getFrame();
  5. };
  6. class CA : public Camera{
  7. private:
  8. A a;//A公司的摄像头对象
  9. public:
  10. void init(){a.initialize();}
  11. void getFrame(){a.grab();}
  12. }
  13. Camera *pCamera = new CA;
  14. pCamera->init();
  15. while(1){
  16. pCamera->getFrame();
  17. /*这里作图像处理*/
  18. }

现在需要换产品了,怎么办?可以再添加一个CB类:

 
  1. class CB : public Camera{
  2. private:
  3. B a;//B公司的摄像头对象
  4. public:
  5. void init(){a.start();}
  6. void getFrame(){a.process();}
  7. };

对于我们的主程序,只需要改动一处即可:

 
  1. //Camera *pCamera = new CA;
  2. //只需改动这一句即可
  3. Camera *pCamera = new CB;
  4. pCamera->init();
  5. while(1){
  6. pCamera->getFrame();
  7. /*这里作图像处理*/
  8. }

后面这种编程的写法,与前一种写法相比,是更容易扩展、更容易升级的。当然要想获得这个好处,必须同时做到 2 点:类的编写者必须提供虚函数,类的使用者必须使用基类的指针或者引用。

因此,当你作为类的编写者编写类的时候,特别是编写继承体系时,必须提供合适的虚函数。当你作为类的使用者使用类的时候,必须优先定义基类的指针或者引用,如果没有特殊理由,绝不使用子类的类型。

另外,注意一点:公有继承的基类的析构函数必须是虚的

所谓纯虚函数。C++ 允许一个类声明一个纯虚函数,即该虚函数未实现,没有函数体。此时,该类是一个抽象类,它一定是作为基类使用的。纯虚函数的作用,就是为其派生类规定函数的原型。这也被称为接口。C++ 中并没有专门描述接口的语法和关键字,C++ 中使用纯虚函数来完成接口的功能(有关接口的内容可以参考第 3 关)。而Java中则将 interface 作为了一个关键字。

顺便提一句,在一个继承体系中,如果基类的某个函数被 virtual 修饰了,其派生类以及派生类的派生类等等都不需要再显式的写出 virtual 关键字,该函数在其所有派生类中均是虚的。

编程要求

根据提示,在右侧编辑器的Begin-End区域内补充代码。

这是Array.List的答案

#include "ArrayList.h"

/****************start from here**********************/


ArrayList::ArrayList()
{
	size = 0;
	data = new int[10];
	capacity = 10;
}

ArrayList::ArrayList(const ArrayList &rhs)
{
	data = new int[rhs.capacity];
	size = rhs.size;
	for (int i = 0; i < size; i++)
	{
		data[i] = rhs.data[i];
	}
}

ArrayList::ArrayList(int const a[], int n)
{
	data = new int[n];
	size = n;
	for (int i = 0; i < size; i++)
	{
		data[i] = a[i];
	}
}

ArrayList::ArrayList(int n, int value)
{
	data = new int[n];
	size = n;
	for (int i; i < size; i++)
	{
		data[i] = value;
	}
}

ArrayList::~ArrayList()
{
	delete[] data;
}

void ArrayList::insert(int pos, int value)
{

	if (size == capacity)
	{
		int *old = data;
		data = new int[2 * size];
		for (int i = 0; i < size; i++)
		{
			data[i] = old[i];
		}
		capacity = 2 * size;
	}
	for (int i = size - 1; i >= pos; i--)
		data[i + 1] = data[i];
	data[pos] = value;
	size++;
}

void ArrayList::remove(int pos)
{
	for (int i = pos; i < size - 1; i++)
		data[i] = data[i + 1];
	size--;
}
int ArrayList::at(int pos) const
{
	return data[pos];
}
void ArrayList::modify(int pos, int newValue)
{
	data[pos] = newValue;
}

这是Linked.List的答案

#include "LinkedList.h"
#include<iostream>
using namespace std;
/****************start from here**********************/
LinkedList::LinkedList() : List(0)
{
	head = new Node();
}

LinkedList::LinkedList(const LinkedList &rhs) : List(rhs.size)
{
	head = new Node;
	Node *p = head;
	for (Node *q = rhs.head->next; q; q = q->next)
	{
		p->next = new Node(q->data, q->next);
		p = p->next;
	}
}

LinkedList::LinkedList(int const a[], int n) : List(n)
{
	head = new Node();
	Node *p = head;
	for (int i = 0; i < size; i++)
	{
		p->next = new Node(a[i], nullptr);
		p = p->next;
	}
}

LinkedList::LinkedList(int n, int value) : List(n)
{
	head = new Node();
	Node *p = head;
	for (int i = 0; i < size; i++)
	{
		p->next = new Node(value, nullptr);
		p = p->next;
	}
}

LinkedList::~LinkedList()
{
	Node *p = head;
	while (head->next != NULL)
	{
		p = head->next;
		delete head;
		head = p;
	}
	delete head;
}

void LinkedList::insert(int pos, int value)
{
	Node *p = head;
	for (int i = -1; i < pos - 1; i++)
	{
		p = p->next;
	}
	Node *q = new Node(value, p->next);
	p->next = q;
	size++;
}
void LinkedList::remove(int pos)
{
	Node *p = head;
	for (int i = -1; i < pos - 1; i++)
	{
		p = p->next;
	}
	Node *q = head;
	for (int j = -1; j < pos + 1; j++)
	{
		q = q->next;
	}
	p->next = q;
	size--;
}

int LinkedList::at(int pos) const
{
	Node *p = head;
	for (int i = -1; i < pos; i++)
	{
		p = p->next;
	}
	return p->data;
}

void LinkedList::modify(int pos, int newValue)
{
	Node *p = head;
	for (int i = -1; i < pos; i++)
	{
		p = p->next;
	}
	p->data = newValue;
}
void LinkedList::disp(ostream &os) const
{
	Node *p = head;
	while (p->next != NULL)
	{
		p = p->next;
		cout << p->data << " ";
	}
	cout << endl;
}

在 C++ 中,虚函数是一种非常重要的概念,它允许子类重写父类的函数,并且在运行时调用正确的函数。 设计虚函数的一般步骤如下: 1. 在基类中定义虚函数使用键字 virtual。 2. 在派生类中重写虚函数,如果不需要重写,则直接继承即可。 3. 在使用时,使用基类指针或引用指向派生类对象,从而调用正确的函数。 下面是一个简单的例子,演示如何使用虚函数: ```c++ #include <iostream> class Shape { public: virtual void draw() { std::cout << "Drawing a shape..." << std::endl; } }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle..." << std::endl; } }; class Square : public Shape { public: void draw() override { std::cout << "Drawing a square..." << std::endl; } }; int main() { Shape* shape1 = new Circle(); Shape* shape2 = new Square(); shape1->draw(); // 输出 "Drawing a circle..." shape2->draw(); // 输出 "Drawing a square..." delete shape1; delete shape2; return 0; } ``` 在这个例子中,我们定义了一个基类 Shape 和两个派生类 Circle 和 Square。在基类 Shape 中定义了一个虚函数 draw,然后在派生类 Circle 和 Square 中重写了这个函数。在主函数中,我们使用基类指针指向派生类对象,然后调用了这个虚函数,从而实现了多态性。 值得注意的是,如果不使用虚函数,那么在使用基类指针或引用指向派生类对象时,将会调用基类中的函数,而不是派生类中的函数。这将导致程序无法实现多态性,而虚函数则解决了这个问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值