我需要一种从单个对象提供不同接口的方法。
例如。用户一应该能够呼叫Foo::bar()
,用户2应该能够呼叫Foo::baz()
,但是用户一不能呼叫Foo::baz()
,而用户二不能呼叫Foo::bar()
。
我确实做到了,但是我认为这不是最佳选择。
class A
{
public:
virtual void bar() = 0;
virtual ~A() = 0;
};
class B
{
public:
virtual void baz() = 0;
virtual ~B() = 0;
};
class Foo : public A, public B
{
public:
Foo() = default;
void baz() override;
void bar() override;
};
class Factory
{
public:
Factory()
{
foo = std::make_shared<Foo>();
}
std::shared_ptr<A> getUserOne()
{
return foo;
}
std::shared_ptr<B> getUserTwo()
{
return foo;
}
private:
std::shared_ptr<Foo> foo;
};
有没有更好的方法来实现这一目标。也许与包装对象。我真的不需要用new
(std::make_shared
)分配这个foo对象,但我什至不愿意,但是我不能使用原始指针,而智能指针会带来不必要的开销和系统调用。
编辑:我会举一个例子。
有车。用户一是驱动程序。他可以操纵方向盘,加速行驶或使用休息。用户二是乘客,例如,他可以控制收音机。
我不希望乘客可以使用休息时间或驾驶员可以使用收音机。
而且它们都在汽车上,因此用户一的动作会对用户二产生影响,反之亦然。
答案 0 :(得分:1)
您真正需要的是两个对象之间的共享数据。继承不是一个很好的选择,因为不仅您不需要is A
关系,而且您明确希望避免这种关系。因此,组成是您的答案,尤其是因为您有工厂:
class Data
{
public:
void bar();
void baz();
};
然后您将使用合成来代替继承:
class A
{
public:
A(Base *base) : mBase(base) {}
void bar() { mBase->bar(); }
private:
Base *mBase = nullptr;
};
//class B would be the same only doing baz()
最后是Factory
:
class Factory
{
public:
A *getUserOne() { return &mA; }
B *getUserTwo() { return &mB; }
private:
Base mBase;
A mA(&mBase);
B mB(&mBase);
};
有关此解决方案的要点。尽管它没有在堆上分配,但只要有用户,就需要保持Factory
处于活动状态。因此,在OP中使用std::shared_ptr
可能是 smart 的主意。 :-)但是,当然伴随着原子引用计数的成本。
第二,A
与B
毫无关系。这是设计使然,不同于原始解决方案,不允许dynamic_cast
和A
之间的B
。
最后实现的位置取决于您。您可以在Data
中拥有所有内容,而只需A
和B
来调用它(如上所示),但也可以将Data
变成struct
仅保存您的数据,并分别在A
和B
中实现您的方法。后者是更“面向数据”的编程,与我传统选择的更为传统的“面向对象”相反,如今它们已经越来越流行。
答案 1 :(得分:0)
您可以单独声明数据
struct Data
{
/* member variables */
};
具有能够处理所述数据的接口类将保护所有成员
class Interface
{
protected:
Interface(Data &data) : m_data{data} {}
void bar() { /* implementation */ }
void baz() { /* implementation */ }
Data &m_data;
};
具有派生于特定公共成员的分类
class A : private Interface
{
public:
A(Data &data) : Interface{data} {}
using Interface::bar;
};
class B : private Interface
{
public:
B(Data &data) : Interface{data} {}
using Interface::baz;
};
这样,您还可以使用户能够重叠访问某些功能,而不必多次实施。
class Admin : private Interface
{
public:
Admin(Data &data) : Interface{data} {}
using Interface::bar;
using Interface::baz;
};
当然,根据您使用数据的方式,您可能需要一个指针或共享指针,并可能在来自多个线程的访问之间添加一些同步。
使用此模型的示例代码:
void test()
{
Data d{};
auto a = A{d};
a.bar();
// a.baz is protected so illegal to call here
auto b = B{d};
b.baz();
// b.bar is protected so illegal to call here
auto admin = Admin{d};
admin.bar();
admin.baz();
}
在我看来这是高效的,因为无论您拥有多少用户类型,您都只有一组数据和一个用于数据处理的单一实现。