Abh*_*yal 17 c++ oop inheritance class c++11
我有一个父类,有两个或更多的子类派生自它.随着更多要求的出现,未来不同子类的数量可能会增加,但它们都将遵循基类方案,并且将包含很少的独特方法.让我举一个例子 -
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class B{
private: int a; int b;
public: B(const int _a, const int _b) : a(_a), b(_b){}
virtual void tell(){ std::cout << "BASE" << std::endl; }
};
class C : public B{
std::string s;
public: C(int _a, int _b, std::string _s) : B(_a, _b), s(_s){}
void tell() override { std::cout << "CHILD C" << std::endl; }
void CFunc() {std::cout << "Can be called only from C" << std::endl;}
};
class D : public B{
double d;
public: D(int _a, int _b, double _d) : B(_a, _b), d(_d){}
void tell() override { std::cout << "CHILD D" << std::endl; }
void DFunc() {std::cout << "Can be called only from D" << std::endl;}
};
int main() {
std::vector<std::unique_ptr<B>> v;
v.push_back(std::make_unique<C>(1,2, "boom"));
v.push_back(std::make_unique<D>(1,2, 44.3));
for(auto &el: v){
el->tell();
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,tell()方法可以正常工作,因为它是虚拟的并且在子类中适当地覆盖.但是现在我无法调用各自类的CFunc()方法和DFunc()方法.所以我脑子里有两个选择 -
CFunc()在子类中的一些已经定义的虚方法中包装和朋友,以便它一起执行.但随着数量的增加,我将无法控制特定方法的特定执行.
或者在基类中提供一些纯虚方法,void process() = 0就像它们喜欢的那样在子类中定义它们.可能会被void process(){}一些人留空并被一些人使用.但是再一次感觉不对,因为我在途中失去了回报价值和参数.也像之前的选项一样,如果某些子类中有更多方法,这种方法感觉不正确.
和另一个 -
dynamic_cast<>?.这会是一个不错的选择 - 回滚父指针指向子指针(顺便说一下,我在这里使用智能指针,所以只unique/shared允许),然后调用所需的函数.但是,我如何区分不同的子类?另一个公共成员可能会返回一些独特的类枚举值?我对这种情况非常缺乏经验,并希望得到一些反馈.我该如何处理这个问题?
Ami*_*ory 11
我有一个父类,有两个或更多的子类派生出来......但随着数量的增加,我将无法控制特殊方法的特殊执行.
另一个选项,当预计方法数量增加时有用,并且派生类预期保持相对稳定,是使用访问者模式.以下用途boost::variant.
假设您从三个课程开始:
#include <memory>
#include <iostream>
using namespace std;
using namespace boost;
class b{};
class c : public b{};
class d : public b{};
Run Code Online (Sandbox Code Playgroud)
b您可以使用变体类型,而不是使用指向基类的(智能)指针:
using variant_t = variant<c, d>;
Run Code Online (Sandbox Code Playgroud)
和变量变量:
variant_t v{c{}};
Run Code Online (Sandbox Code Playgroud)
现在,如果您想以不同的方式处理c和d方法,您可以使用:
struct unique_visitor : public boost::static_visitor<void> {
void operator()(c c_) const { cout << "c" << endl; };
void operator()(d d_) const { cout << "d" << endl; };
};
Run Code Online (Sandbox Code Playgroud)
你打电话给谁
apply_visitor(unique_visitor{}, v);
Run Code Online (Sandbox Code Playgroud)
请注意,您也可以使用相同的机制来统一处理所有类型,方法是使用接受基类的访问者:
struct common_visitor : public boost::static_visitor<void> {
void operator()(b b_) const { cout << "b" << endl; };
};
apply_visitor(common_visitor{}, v);
Run Code Online (Sandbox Code Playgroud)
请注意,如果类的数量增加的速度快于方法的数量,则此方法将导致维护问题.
完整代码:
#include "boost/variant.hpp"
#include <iostream>
using namespace std;
using namespace boost;
class b{};
class c : public b{};
class d : public b{};
using variant_t = variant<c, d>;
struct unique_visitor : public boost::static_visitor<void> {
void operator()(c c_) const { cout << "c" << endl; };
void operator()(d d_) const { cout << "d" << endl; };
};
struct common_visitor : public boost::static_visitor<void> {
void operator()(b b_) const { cout << "b" << endl; };
};
int main() {
variant_t v{c{}};
apply_visitor(unique_visitor{}, v);
apply_visitor(common_visitor{}, v);
}
Run Code Online (Sandbox Code Playgroud)
您可以为每个设备类声明具有纯方法的接口.定义特定设备实现时,仅从对其有意义的接口继承.
使用您定义的接口,您可以迭代并调用特定于每个设备类的方法.
在下面的示例中,我声明了一个HardwareInterface将由所有设备继承的设备,并且AlertInterface只能由可以物理警告用户的硬件设备继承该设备.其它类似的接口可以被定义,例如SensorInterface,LEDInterface等
#include <iostream>
#include <memory>
#include <vector>
class HardwareInteface {
public:
virtual void on() = 0;
virtual void off() = 0;
virtual char read() = 0;
virtual void write(char byte) = 0;
};
class AlertInterface {
public:
virtual void alert() = 0;
};
class Buzzer : public HardwareInteface, public AlertInterface {
public:
virtual void on();
virtual void off();
virtual char read();
virtual void write(char byte);
virtual void alert();
};
void Buzzer::on() {
std::cout << "Buzzer on!" << std::endl;
}
void Buzzer::off() {
/* TODO */
}
char Buzzer::read() {
return 0;
}
void Buzzer::write(char byte) {
/* TODO */
}
void Buzzer::alert() {
std::cout << "Buzz!" << std::endl;
}
class Vibrator : public HardwareInteface, public AlertInterface {
public:
virtual void on();
virtual void off();
virtual char read();
virtual void write(char byte);
virtual void alert();
};
void Vibrator::on() {
std::cout << "Vibrator on!" << std::endl;
}
void Vibrator::off() {
/* TODO */
}
char Vibrator::read() {
return 0;
}
void Vibrator::write(char byte) {
/* TODO */
}
void Vibrator::alert() {
std::cout << "Vibrate!" << std::endl;
}
int main(void) {
std::shared_ptr<Buzzer> buzzer = std::make_shared<Buzzer>();
std::shared_ptr<Vibrator> vibrator = std::make_shared<Vibrator>();
std::vector<std::shared_ptr<HardwareInteface>> hardware;
hardware.push_back(buzzer);
hardware.push_back(vibrator);
std::vector<std::shared_ptr<AlertInterface>> alerters;
alerters.push_back(buzzer);
alerters.push_back(vibrator);
for (auto device : hardware)
device->on();
for (auto alerter : alerters)
alerter->alert();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
根据各个传感器类型AccelerometerInterface,接口可以更加具体:GyroscopeInterface,等等.
虽然你的要求是可能的,但它会导致你的代码散布在演员表中,或者在没有意义的类上可用的函数.两者都不可取.如果您需要知道它是C类还是D类,那么很可能将它存储为B是错误的,或者您的接口B是错误的.
多态性的全部意义在于使用B的事情是他们不需要确切地知道它是什么类型的B. 对我来说,这听起来像是在扩展课程而不是将它们作为成员,即"C是B"没有意义,但"C有B做".
我会回过头来重新考虑B,C,D和所有未来项目的作用,以及为什么他们拥有你需要调用的这些独特功能; 并研究函数重载是否是您真正想要做的.(类似于Ami Tavory对访客模式的建议)
小智 6
你可以unique_ptr.get()用来获取Unique Pointer中的指针,并使用指针作为normall.像这样:
for (auto &el : v) {
el->tell();
D* pd = dynamic_cast<D*>(el.get());
if (pd != nullptr)
{
pd->DFunc();
}
C* pc = dynamic_cast<C*>(el.get());
if (pc != nullptr)
{
pc->CFunc();
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
CHILD C
Can be called only from C
CHILD D
Can be called only from D
Run Code Online (Sandbox Code Playgroud)
如果可以隐藏尽可能多的特定于类型的实现细节,则应使用第一种方法.
然后,如果你需要公共接口,你应该使用虚拟功能(你的第二种方法),并避免使用dynamic_cast(你的第三种方法).许多theads可以告诉你为什么(例如Polymorphism vs DownCasting).你已经提到了一个很好的理由,就是你不应该真正检查对象类型......
如果您的虚拟功能有问题,因为您的驱动类有太多独特的公共接口,那么它不是IS-A关系,是时候审查您的设计了.例如,对于共享功能,请考虑组合,而不是继承......
| 归档时间: |
|
| 查看次数: |
2446 次 |
| 最近记录: |