C++ Primer Plus 学习笔记 第十四章 保护继承 多重继承
保护继承
保护继承会将基类的共有成员和保护成员继承为派生类的保护成员
如果派生保护或派生私有的,又希望在派生类之外使用该类的方法。咋搞?
- 新建函数,然后在函数中调用临时基类对象
第二种方法,
在声明中直接使用using声明使用基类的某个函数
这样不管const还是非const的版本都可以用。
以上方法只适用于继承而不是包含。
多重继承:
每个继承的基类都需要单独声明范围限定(public,private, protected)
多继承示例:
Worker0.h
#ifndef WORKER0_H_
#define WORKER0_H_
#include <string>
class Worker
{
private:
std::string fullname;
long id;
public:
Worker() : fullname("no one") {}
Worker(const std::string & s, long n) : fullname(s), id(n) {}
virtual ~Worker() = 0;
virtual void Set();
virtual void Show() const;
};
class Waiter: public Worker
{
private:
int panache;
public:
Waiter() : Worker(), panache(0) {}
Waiter(const std::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 : public Worker
{
protected:
enum{other, alto, contralto, soprano, base, baritone, tenor};
enum {Vtypes = 7};
private:
// 这里需要使用const声明 否则编译器会出警告
static const char *pv[Vtypes];
int voice;
public:
Singer() : Worker(), voice(other) {}
Singer(const std::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;
};
#endif
Worker0.cpp
#include "Worker0.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
Worker::~Worker(){}
void Worker::Set()
{
cout << "Enter worker's name: ";
getline(cin, fullname);
cout <<"Enter worker's ID: ";
cin >> id;
while(cin.get() != '\n')
continue;
}
void Worker::Show() const
{
cout << "Name: " << fullname << "\n";
cout << "Employee ID: " << id << "\n";
}
void Waiter::Set()
{
Worker::Set();
cout << "Enter waiter's panache rating: ";
cin >> panache;
while(cin.get() != '\n')
continue;
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Show();
cout << "Panache rating: " << panache << "\n";
}
// 这里需要使用const声明 否则编译器会出警告
const char* Singer::pv[] = {"other", "alto", "contralto", "soprano", "bass", "baritone", "tenor"};
void Singer::Set()
{
Worker::Set();
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 <<endl;
while (cin >> voice && (voice < 0 || voice >= Vtypes))
cout << "Please enter a value >=0 and < " << Vtypes << endl;
}
void Singer::Show() const
{
cout << "Category: singer\n";
Worker::Show();
cout << "Vocal range: " << pv[voice] << endl;
}
worktest.cpp
#include <iostream>
#include "Worker0.h"
const int LIM = 4;
int main()
{
Waiter bob("Bob Apple", 314L, 5);
Singer bev("Beverly Hills", 522L, 3);
Waiter w_temp;
Singer s_temp;
Worker* pw[LIM] = {&bob, &bev, &w_temp, &s_temp};
int i;
for (i =2; i < LIM; i++)
pw[i]->Set();
for (i = 0; i < LIM; i++)
{
pw[i]->Show();
std::cout << std::endl;
}
return 0;
}
运行结果
那么问题来了
多重继承后每个派生类中都会有基类的组件。然后派生类的派生类要调用基类的函数时就不知道要调用哪个了。因为这时候有多个基类组件。
咋搞
或者 使用虚基类 共享一个基类对象
格式:
class Singer:: virtual public Woker{}; or class Singer:: public virtual Woker{};
虚基类会使得程序完成一些额外的运算。
用了虚基类的话 构造函数的规则就需要更改
派生类的派生类不直接调用基类的构造函数 而是调用指定父类的构造函数。
然后在构造函数定义的时候代表的意思会有变化
也就是说子类通过初始化成员列表来调用父类的构造函数,父类在调用基类的构造函数 来初始化时行不通了
wk没办法这样传递到基类的构造函数。那这时候编译器会使用基类的默认构造函数。 那就不是我们想要的了。
那咋搞?
显示的调用基类的构造函数初始化。这种方法只适用于虚基类 非虚基类这么干的话是非法的。
这是构造函数的问题, 还有一个问题就是同名函数,用哪个父类的?(基类其实也是父类, 嗯。。爷爷类吧)
一种方法是可以使用作用域解析运算符来显式指明用哪个父类的函数
第二种方法是 在子类定义一个同名函数。然后在函数中指明调用哪个父类的同名函数
但如果这时候需要用到多个父类的Show方法就会出问题。
因为多个父类方法都调用了基类的Show方法。 就会有数据重复处理或者输出(参考上面的程序示例的Show()方法)
咋搞?
一种方法:
试用模块化处理,而不是递增:
另一种办法是吧所有的数据组件都设置为保护而不是私有。但是用保护方法能够更好的控制数据的访问。
Set()函数的问题和处理方法类似。
程序示例:
workermi.h
#ifndef WORKERMI_H_
#define WORKERMI_H_
#include <string>
class Worker
{
private:
std::string fullname;
long id;
protected:
virtual void Data() const;
virtual void Get();
public:
Worker() : fullname("no one"), id(0L) {}
Worker(const std::string & s, long n) : fullname(s), id(n) {}
virtual ~Worker() = 0;
virtual void Set() = 0;
virtual void Show() const = 0;
};
class Waiter: virtual public Worker
{
private:
int panache;
protected:
void Data() const;
void Get();
public:
Waiter() : Worker(), panache(0) {}
Waiter(const std::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, base, 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 std::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() {}
SingingWaiter(const std::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 & wt, int p = 0)
: Worker(wt), Waiter(wt, p), Singer(wt) {}
SingingWaiter(const Singer & wt, int p = 0)
: Worker(wt), Waiter(wt, p), Singer(wt) {}
void Set();
void Show() const;
};
#endif
workermi.cpp
#include "Workermi.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
Worker::~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() != '\n')
continue;
}
// Waiter methods
void Waiter::Set()
{
cout << "Enter waiter's name: ";
Worker::Get();
Get();
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Data();
Data();
}
void Waiter::Data() const
{
cout << "Panache rating: " << panache << endl;
}
void Waiter::Get()
{
cout << "Enter waiter's panache rating: ";
cin >> panache;
while(cin.get() != '\n')
continue;
}
char const * Singer::pv[Singer::Vtypes] = {"other", "alto", "contralto", "soprano", "bass", "baritone", "tenor"};
void Singer::Set()
{
cout << "Enter singer's name: ";
Worker::Get();
Get();
}
void Singer::Show() const
{
cout << "Gategory: singer\n";
Worker::Data();
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;
}
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();
}
void SingingWaiter::Show() const
{
cout << "Category: singing waiter\n";
Worker::Data();
Data();
}
workmi.cpp
#include <iostream>
#include <cstring>
#include "workermi.h"
const int SIZE = 5;
int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::strchr;
Worker * lolas[SIZE];
int ct;
for (ct = 0; ct < SIZE; ct++)
{
char choice;
cout << "Enter the employee category:\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;
break;
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];
cout << "Bye.\n";
return 0;
}
多重继承的其他变态情况
咋处理:
如果是多重继承的话 C++的二义性规则会发生变化
优先使用派生类的同名成员
类C的q()优于类B中的q() 但是 两个omg()会产生二义性。因为他们C和E是平级。
还没有评论,来说两句吧...