我正在编写一个C ++库,我想隐藏基类的名称,即我不希望用户能够使用基类指针(或引用)来引用派生类。 / p>
假设我有一个班级
class Message
{
public:
//...
};
和两个派生类:
class SpecialMessage : public Message
{
//..
};
class NeatMessage : public Message
{
//..
};
如何使用户无法执行以下操作:
SpecialMessage specialMessage{};
Message* baseHandle = &specialMessage;
我想排除这种行为的一个原因是我不想声明Message的析构函数是虚拟的。
我想过将Message隐藏在详细的命名空间中:
namespace detail
{
class Message
{
};
}
但这有点奇怪,如果有这么倾向,用户仍然可以访问Message类。
编辑:
私有继承对我不起作用,因为我仍然希望用户能够访问Message的方法(通过派生类的对象)。组合也不会 - 因为为了使用户可以访问Message的方法,我必须在派生类中编写包装器代码。
声明基类的析构函数是受保护的(正如Sebastian Redl所建议的那样)虽然是一个有趣的想法,但也不适合我,因为它仍然允许用户获得基类句柄。
答案 0 :(得分:3)
您可以私下继承,也可以在Message
内嵌入SpecialMessage
对象,而不是派生。
或者,如果您从未真正需要原始Message
个对象,则可以使Message
析构函数受到保护。
答案 1 :(得分:2)
使用受保护或私有继承而不是公共:
class SpecialMessage : private Message {};
或者,为了解决您没有虚拟析构函数的真正问题,只需使Message
的析构函数受到保护即可。然后,即使它们可以创建基类指针,它们也不能通过它非多态地破坏对象。
我必须要求您考虑您的设计:为什么您想要一个Message
界面?如果基类完全是非多态的,那么绝对应该使用组合或私有继承而不是公共继承。公共继承仅用于替换,而不是实现。
答案 2 :(得分:0)
偏好composition over inheritance。如果你继承自Message
,我无法想到从用户那里隐藏它的声明。当然你可以像你建议的那样模糊它,但声明必须在那里 - 期间。
如果您使用Message
作为私人成员但未发送图书馆的源代码,则用户将看不到该声明。要让您的用户能够访问Message
的功能,请使用facade pattern.
答案 3 :(得分:0)
可能有更好的方法来提供您使用不同架构所需的语义。但是,要解决此问题,您可以随时执行一些粗略的模板魔术:
#include <iostream>
class Message
{
public:
void Foo() {}
int Bar(int i) {return i + 10;}
};
class SpecialMessage
{
private:
Message message;
public:
template<typename T, typename... Args>
auto MessageCall(T t, Args... args) -> decltype((message.*t)(args...))
{
return (message.*t)(args...);
}
};
int main()
{
SpecialMessage m;
m.MessageCall(&Message::Foo);
std::cout << "\n test: " << m.MessageCall(&Message::Bar, 10);
return 0;
}
有点脏,是的......