多重继承,虚基类,模块化方法,strchr函数的使用,纯虚函数的注意点--抽象基类的派生类如何实例化,虚基类的优先级
//class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
#include<string>
using namespace std;
class Worker //抽象基类(包含纯虚函数的类)
{
private:
string fullname;
long id;
protected:
virtual void Data() const;
virtual void Get();
public:
Worker() :fullname("no one"), id(0L) { };
Worker(const string& s, long n) :fullname(s), id(n) { };
//纯虚函数
virtual ~Worker() = 0;
virtual void Set() = 0;
virtual void Show()const = 0;
};
//虚基类(用于MI多重继承)
class Waiter :virtual public Worker
{
private:
int panache;
protected:
void Data()const;
void Get();
public:
Waiter() :Worker(), panache(0){ };
Waiter(const string& s, long n, int p = 0) : Worker(s, n), panache(p) { }; //默认参数从右往左开始设置
Waiter(const Worker& wk, int p = 0) :Worker(wk), panache(p) { };
void Set();
void Show()const;
};
class Singer :virtual public Worker
{
protected:
//创建类常量
enum {other, alto, contralto, soprano, bass, baritone, tenor };
enum {Vtypes = 7};
void Data()const;
void Get();
private:
static const char* pv[Vtypes]; //静态变量,如果没有在定义的时候就初始化,则需要在实现文件中标明所属类中再进行初始化
int voice;
public:
Singer() :Worker(), voice(other) { };
Singer(const string& s, long n, int v = other) :Worker(s, n), voice(v) { };
Singer(const Worker& wk, int v = other) :Worker(wk), voice(v) { };
void Set();
void Show()const;
};
class SingingWaiter :public Singer, public Waiter
{
protected:
void Data()const;
void Get();
public:
SingingWaiter() { };
//MI的继承类需要显示的调用基类的构造函数,否则将自动调用基类的默认构造函数
SingingWaiter(const string& s, long n, int p = 0, int v = other) :Worker(s, n), Waiter(s, n, p), Singer(s, n, v) { };
SingingWaiter(const Worker& wk, int p = 0, int v = other) :Worker(wk), Waiter(wk, p), Singer(wk, v) { };
//参数类型为基类对象引用和指针时,传递的参数可以是其继承类对象的引用和指针
SingingWaiter(const Waiter& wk, int v = other) :Worker(wk), Waiter(wk), Singer(wk, v) { }; //此中的Waiter构造函数本应该接受两个参数,但其1第二个参数是一个默认参数,可以不传递参数.
SingingWaiter(const Singer& wk, int p = 0) :Worker(wk), Waiter(wk, p), Singer(wk) { };
void Set();
void Show()const;
};
#endif
//fuc.cpp
#include"class.h"
#include<iostream>
//Worker:
void Worker::Data()const
{
cout << "Name: " << fullname << endl;
cout << "Employee ID: " << id << endl;
}
void Worker::Get()
{
getline(cin, fullname);
cout << "Enter worker's ID: ";
cin >> id;
while (cin.get() != '\0')
{
continue;
}
}
Worker::~Worker()
{
}
//Waiter:
void Waiter::Set()
{
cout << "Enter waiter's name: ";
Worker::Get();
Data();
}
void Waiter::Show()const
{
cout << "Catagory: waiter" << endl;
Worker::Data();
Data(); //Waiter::Data()
}
void Waiter::Data()const
{
cout << "Panache rating: " << panache << endl;
}
void Waiter::Get()
{
cout << "Enter waiter's panache rating: ";
cin >> panache;
while (cin.get() != '\0')
{
continue;
}
}
//Singer:
//对名字限定(通过类名和成员解析运算符)以确定其所属类
const char* Singer::pv[Singer::Vtypes] = { "other","alto","contralto","soprano","bass","baritone","tenor" };
void Singer::Set()
{
cout << "Enter singer's name: ";
Worker::Get();
Get(); //Singer::Get()
}
void Singer::Show()const
{
cout << "Catagory: singet\n";
Worker::Data();
Data(); //Singer::Data()
}
void Singer::Data()const
{
cout << "Vocal range: " << pv[voice] << endl;
}
void Singer::Get()
{
cout << "Enter number for singer's vocal range:\n";
int i;
for (i = 0; i < Vtypes; i++)
{
cout << i << ": " << pv[i] << " ";
if(i % 4 == 3)
{
cout << endl;
}
}
if(i % 4 != 0)
{
cout << '\n';
}
cin >> voice;
while (cin.get() != '\n')
{
continue;
}
}
//SingingWaiter
void SingingWaiter::Data()const
{
Singer::Data();
Waiter::Data();
}
void SingingWaiter::Get()
{
Waiter::Get();
Singer::Get();
}
void SingingWaiter::Set()
{
cout<<"Enter singing waiter's name: ";
Worker::Get();
Get(); // SingingWaiter::Get()
}
void SingingWaiter::Show()const
{
cout << "Catagory: singing waiter\n";
Worker::Data();
Data(); //SingingWaiter::Data()
}
//main.cpp
#include"class.h"
#include<iostream>
#include<cstring>
const int SIZE = 5;
int main()
{
Worker* lolas[SIZE]; //基类指针数组,可以指向派生类指针
int ct;
for (ct = 0; ct < SIZE; ct++)
{
char choice;
cout << "Enter the employee catagory: \n"
<< "w: waiter s:singer "
<< "t: singing waiter q:quit \n";
cin >> choice;
while (strchr("wstq", choice) == NULL)
{
cout << "Please enter a w, s, t, or q";
cin >> choice;
}
if (choice == 'q')
{
break;
}
switch (choice)
{
case 'w':
lolas[ct] = new Waiter;
break;
case 's':
lolas[ct] = new Singer;
case 't':
lolas[ct] = new SingingWaiter;
break;
}
cin.get();
lolas[ct]->Set();
}
cout << "\nHere is your staff:\n";
int i;
for (i = 0; i < ct; i++)
{
cout << endl;
lolas[i]->Show();
}
for (i = 0; i < ct; i++)
{
delete lolas[i]; // new 和 delete 配套 new -- delete new [] -- delete []
}
cout << "Bye~" << endl;
system("pause");
return 0;
}
知识点:
1.引用多重继承的同时应当引用虚基类:虚基类使得从多个类派生出的对象只继承一个基类对象。
eg:
class Singer : virtual public Worker
class Waiter : public virtual worker
//virtual和public的顺序无关紧要
2.使用虚基类的构造函数规则
1):C++在基类是虚的时候,将禁止信息通过中间类自动传递给基类,因此应当显式的调用所需的基类构造函数,否则将使用默认的基类构造函数。
eg:
SingingWaiter() { };
//MI的继承类需要显示的调用基类的构造函数,否则将自动调用基类的默认构造函数
SingingWaiter(const string& s, long n, int p = 0, int v = other) :Worker(s, n), Waiter(s, n, p), Singer(s, n, v) { };
SingingWaiter(const Worker& wk, int p = 0, int v = other) :Worker(wk), Waiter(wk, p), Singer(wk, v) { };
//参数类型为基类对象引用和指针时,传递的参数可以是其继承类对象的引用和指针
SingingWaiter(const Waiter& wk, int v = other) :Worker(wk), Waiter(wk), Singer(wk, v) { }; //此中的Waiter构造函数本应该接受两个参数,但其1第二个参数是一个默认参数,可以不传递参数.
SingingWaiter(const Singer& wk, int p = 0) :Worker(wk), Waiter(wk, p), Singer(wk) { };
2):使用模块化方法来代替单继承的递增思想:即提供不同的基类组件,并在继承类中创建方法将这些组件组合起来。(派生类有有多个“父亲“,直接调用的函数可能在这些父亲中都存在,会出现多义性,所以需要使用模块化编程来指定特定的函数)
eg:
void SingingWaiter::Data()const
{
Singer::Data();
Waiter::Data();
}
void SingingWaiter::Get()
{
Waiter::Get();
Singer::Get();
}
void SingingWaiter::Set()
{
cout<<"Enter singing waiter's name: ";
Worker::Get();
Get(); // SingingWaiter::Get()
}
void SingingWaiter::Show()const
{
cout << "Catagory: singing waiter\n";
Worker::Data();
Data(); //SingingWaiter::Data()
}
3.cstring头文件中的strchr(a,b)函数,其中a为字符串,b为字符:该函数返回字符b在字符串a中第一次出现时的地址,若未出现则返回NULL。
4.类中包含纯虚函数时,该类为抽象基类,同时让派生类去实现纯虚函数,当虚基类的派生类在有纯虚函数未被定义之前不能进行实例化,必须实现所有纯虚函数后才能被实例化。(要确保同一虚函数在不同类中的声明完全一致)
参考文章:(9条消息) c++ 错误提示:不能使用抽象类对象_后青春了的博客-CSDN博客
5.虚基类的优先级:如果使用某个在多个类中均有声明的函数,非虚基类将会产生二义性的错误,而虚基类则会根据优先级选择函数。
优先级:派生类 > 直接/间接祖先类