天天看點

c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

目錄

  • 前言
  • 虛函數
  • 虛基類
  • 虛繼承與多繼承
  • 題目
    • 題目1
      • 輸入
      • 輸出
      • 樣例輸入
      • 樣例輸出
      • 代碼
    • 題目2
      • 輸入
      • 輸出
      • 樣例輸入
      • 樣例輸出
      • 代碼

前言

繼續複習c++

虛函數

使用虛函數使得不同派生類使用基類指針調用時能夠得到行為一緻性的保證。這聽起來有點抽象,意思是:

如果将派生類位址指派給基類指針,再調用派生類中的函數,會失效,還是會調用基類中的同名函數。而基類中使用virtual關鍵字修飾的函數,在指針調用時則不會有這個問題。

詳細請看:C++虛函數簡單講解

未使用虛函數,不能正确調用

class animal
{
public:
	void speak(){cout<<"animal speak!"<<endl;}	
};

class cat : public animal
{
public:
	void speak(){cout<<"cat speak!"<<endl;}		
};

int main()
{
	animal* a = new cat();
	a->speak();

	return 0;
}
           
c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

使用虛函數,正确調用實作多态

class animal
{
public:
	virtual void speak(){cout<<"animal speak!"<<endl;}	
};

class cat : public animal
{
public:
	void speak(){cout<<"cat speak!"<<endl;}		
};

int main()
{
	animal* a = new cat();
	a->speak();

	return 0;
}
           
c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

虛基類

虛基類又名抽象類,隻包含虛函數。虛函數未被實作,需要用

xxx = 0

來修飾

繼承一個虛基類需要實作其所有的虛函數,這起到了規範化的作用,因為有些對象必須實作特定的函數,否則無法正确運作。

如果未實作虛基類所有虛函數,那麼會報錯。

c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

下面的代碼示範了繼承一個抽象類,并且實作其抽象方法的過程

class animal
{
public:
	virtual void speak() = 0;
};

class cat : public animal
{
public:
	void speak(){cout<<"cat speak!"<<endl;}		
};

int main()
{
	animal* a = new cat();
	a->speak();

	return 0;
}
           

虛繼承與多繼承

不同于其他面向對象語言,c++支援多繼承,即一個類可以繼承很多基類。

在繼承時,使用逗号分隔,即可實作繼承多個對象。繼承的本質是将基類對象作為成員建立。如下圖展示了多繼承時基類的建立關系

class cat
{
public:
	cat(){cout<<"cat對象被建立"<<endl;}
};

class people
{
public:
	people(){cout<<"people對象被建立"<<endl;}
};

class CatPeople : public cat, people
{
public:
	CatPeople(){cout<<"CatPeople對象被建立"<<endl;}
};

int main()
{
	CatPeople cp;

	return 0;
}
           
c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

可是如果有如下的繼承結構,即多繼承的基類AB繼承自同一基類BASE

那麼BASE會被建立兩次。詳情請:C++虛繼承簡單講解。如下的代碼示範了這一特性:

(其中BASE=animal,A=cat,B=people,C=CatPeople)

class animal
{
public:
	animal(){cout<<"animal對象被建立"<<endl;}
};

class cat : public animal
{
public:
	cat(){cout<<"cat對象被建立"<<endl;}
};

class people : public animal
{
public:
	people(){cout<<"people對象被建立"<<endl;}
};

class CatPeople : public cat, people
{
public:
	CatPeople(){cout<<"CatPeople對象被建立"<<endl;}
};

int main()
{
	CatPeople cp;

	return 0;
}
           
c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

這造成了額外的開銷,而且當animal類的變量需要指派的時候,會發生歧義問題,即不知道指派給哪一個animal對象。

c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

如圖:歧義問題的編譯報錯

c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

如果用virtual來修飾繼承,即虛繼承,那麼将會隻建立一次基類變量,指派也不存在歧義問題:

class animal
{
public:
	int age;
	animal(){cout<<"animal對象被建立"<<endl;}
};

class cat : virtual public animal
{
public:
	cat(){cout<<"cat對象被建立"<<endl;}
};

class people : virtual public animal
{
public:
	people(){cout<<"people對象被建立"<<endl;}
};

class CatPeople : public cat, people
{
public:
	CatPeople(){cout<<"CatPeople對象被建立"<<endl; age=123;}
};

int main()
{
	CatPeople cp;

	return 0;
}
           
c++複習(3)虛函數,虛基類,虛繼承與多繼承前言虛函數虛基類虛繼承與多繼承題目

題目

題目1

編寫一個程式,定義抽象基類Shape,在Shape類中定義虛函數Area();由它派生出3個派生類:Circle(圓形)、Square(正方形)、Rectangle(矩形)。用虛函數分别計算幾種圖形面積。

1、要求輸出結果保留兩位小數。

2、要求用基類指針數組,使它每一個元素指向每一個派生類對象。

輸入

測試資料的組數 t

第一組測試資料中圓的半徑

第一組測測試資料中正方形的邊長

第一組測試資料中矩形的長、寬

第 t 組測試資料中圓的半徑

第 t 組測測試資料中正方形的邊長

第 t 組測試資料中矩形的長、寬

輸出

第一組資料中圓的面積

第一組資料中正方形的面積

第一組資料中矩形的面積

第 t 組資料中圓的面積

第 t 組資料中正方形的面積

第 t 組資料中矩形的面積

樣例輸入

2
1.2
2.3
1.2 2.3
2.1
3.2
1.23 2.12
           

樣例輸出

4.52
5.29
2.76
13.85
10.24
2.61
           

代碼

#include <iostream>
#include <string>
#include <algorithm>
#include <map>
#include <cmath> 
#include <iomanip>
#include <vector>
#include <set>

#define PI 3.1415926

using namespace std;

class Shape
{
public:
	virtual double Area() = 0;
};

class Circle : public Shape
{
private:
	double r;
public:
	Circle(){}
	Circle(double R){r=R;}
	virtual double Area(){return PI*r*r;}
};

class Square : public Shape
{
private:
	double r;
public:
	Square(){}
	Square(double R){r=R;}
	virtual double Area(){return r*r;}
};

class Rectangle : public Shape
{
private:
	double w,h;
public:
	Rectangle(){}
	Rectangle(double W, double H){w=W;h=H;}
	virtual double Area(){return w*h;}
};

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		Shape* shapes[3];
		double r,w,h;
		cin>>r;
		shapes[0] = new Circle(r);
		cin>>r;
		shapes[1] = new Square(r);
		cin>>w>>h;
		shapes[2] = new Rectangle(w,h);
		
		for(int i=0; i<3; i++)
		{
			cout<<fixed<<setprecision(2)<<shapes[i]->Area()<<endl;
		}
	}

	return 0;
}
           

題目2

1、建立如下的類繼承結構:

  1. 定義一個人員類CPeople,其屬性(保護類型)有:姓名、性别、年齡;
               
  2. 從CPeople類派生出學生類CStudent,添加屬性:學号和入學成績;
               
  3. 從CPeople類再派生出教師類CTeacher,添加屬性:職務、部門;
               
  4. 從CStudent和CTeacher類共同派生出在職研究所學生類CGradOnWork,添加屬性:研究方向、導師;
               

2、分别定義以上類的構造函數、輸出函數print及其他函數(如需要)。

3、在主函數中定義各種類的對象,并測試之。

輸入

第一行:姓名性别年齡

第二行:學号成績

第三行:職務部門

第四行:研究方向導師

輸出

第一行:People:

第二行及以後各行:格式見Sample

樣例輸入

wang-li m 23
2012100365 92.5
assistant computer
robot zhao-jun
           

樣例輸出

People:
Name: wang-li
Sex: m
Age: 23

Student:
Name: wang-li
Sex: m
Age: 23
No.: 2012100365
Score: 92.5

Teacher:
Name: wang-li
Sex: m
Age: 23
Position: assistant
Department: computer

GradOnWork:
Name: wang-li
Sex: m
Age: 23
No.: 2012100365
Score: 92.5
Position: assistant
Department: computer
Direction: robot
Tutor: zhao-jun
           

代碼

#include <iostream>
#include <string>
#include <algorithm>
#include <map>
#include <cmath> 
#include <iomanip>
#include <vector>
#include <set>

#define PI 3.1415926

using namespace std;

class People
{
protected:
	string name, sex, age;
public:
	People(){}	
	People(string Name, string Sex, string Age){name=Name;sex=Sex;age=Age;}
	friend ostream& operator << (ostream& os, People& p)
	{
		cout<<"People:"<<endl;
		cout<<"Name: "<<p.name<<endl;
		cout<<"Sex: "<<p.sex<<endl;
		cout<<"Age: "<<p.age<<endl;
	}
};

class Student : virtual public People
{
protected:
	string number, score;	
public:
	Student(){}
	Student(string na, string se, string ag, string nu, string sc)
	{
		name=na; sex=se; age=ag; number=nu; score=sc;
	}
	friend ostream& operator << (ostream& os, Student& p)
	{
		cout<<"Student:"<<endl;
		cout<<"Name: "<<p.name<<endl;
		cout<<"Sex: "<<p.sex<<endl;
		cout<<"Age: "<<p.age<<endl;
		cout<<"No.: "<<p.number<<endl;
		cout<<"Score: "<<p.score<<endl;
	}
};

class Teacher : virtual public People
{
protected:
	string department, position;	
public:
	Teacher(){}
	Teacher(string na, string se, string ag, string de, string po)
	{
		name=na; sex=se; age=ag; department=de; position=po;
	}
	friend ostream& operator << (ostream& os, Teacher& p)
	{
		cout<<"Teacher:"<<endl;
		cout<<"Name: "<<p.name<<endl;
		cout<<"Sex: "<<p.sex<<endl;
		cout<<"Age: "<<p.age<<endl;
		cout<<"Position: "<<p.position<<endl;
		cout<<"Department: "<<p.department<<endl;
	}
};

class GardOnWork : public Teacher, Student
{
protected:
	string direction, tutor;
public:
	GardOnWork(){}
	GardOnWork(string na,
			   string se, 
			   string ag, 
			   string nu, 
			   string sc, 
			   string de, 
			   string po,
			   string di,
			   string tu)
	{
		name=na; sex=se; age=ag; number=nu; score=sc; department=de; position=po; direction=di; tutor=tu;
	}
	friend ostream& operator << (ostream& os, GardOnWork& p)
	{
		cout<<"Teacher:"<<endl;
		cout<<"Name: "<<p.name<<endl;
		cout<<"Sex: "<<p.sex<<endl;
		cout<<"Age: "<<p.age<<endl;
		cout<<"No.: "<<p.number<<endl;
		cout<<"Score: "<<p.score<<endl;
		cout<<"Position: "<<p.position<<endl;
		cout<<"Department: "<<p.department<<endl;
		cout<<"Direction: "<<p.direction<<endl;
		cout<<"Tutor: "<<p.tutor<<endl;
	}
};

int main()
{
	string na, se, ag, nu, sc, de, po, di, tu;
	cin>>na>>se>>ag>>nu>>sc>>po>>de>>di>>tu;
	
	People p1 = People(na, se, ag);
	cout<<p1<<endl;
	
	Student p2 = Student(na, se, ag, nu, sc);
	cout<<p2<<endl;
	
	Teacher p3 = Teacher(na, se, ag, de, po);
	cout<<p3<<endl;
	
	GardOnWork p4 = GardOnWork(na, se, ag, nu, sc, de, po, di, tu);
	cout<<p4<<endl;

	return 0;
}
           

繼續閱讀