我意识到这个问题很老,但它有很多观点,如果你是接口的作者,有一个干净的方法来解决这个问题。
许多人认为虚拟接口应该具有公共的非虚拟功能,在内部遵循私有的虚拟功能(我同意他们的观点)。这有一些优点,其中之一是非虚拟名称可以具有不同的含义,因为它们与接口的绑定更加紧密:
struct BaseA
{
virtual ~BaseA() = default;
void func()
{
handle_a_func();
}
private:
virtual void handle_a_func() = 0;
};
struct BaseB
{
virtual ~BaseB() = default;
int func() const // note the different signature and return type
{
handle_b_func();
}
private:
virtual int handle_b_func() const = 0;
};
// now provide an implementation
struct ABImpl : public BaseA, public BaseB
{
ABImpl() {}
private:
void handle_a_func() override final
{
// alter some state
}
int handle_b_func() const override final
{
return _x;
}
int _x = 0;
};
// now use the class
auto ab = make_shared<ABImpl>();
auto a = static_pointer_cast<BaseA>(ab);
auto b = static_pointer_cast<const BaseB>(ab);
a->func(); // uses A's mutable interface
auto x = b->func(); // uses B's const interface
这种方法的另一个优点是基类实现可以代表派生函数管理诸如互斥锁和哨兵之类的东西,例如:
struct base {
void do() {
std::unique_lock<std::mutex> l(_m);
handle_do();
}
private:
virtual void handle_do() = 0;
std::mutex _m;
};
另一个优点是一些有用的自由函数运算符只需要为整个类层次结构定义一次:
struct base
{
void write_to(std::ostream& os) const {
// lock a mutex here?
handle_write(os);
private:
virtual void handle_write(std::ostream& os) const {
os << "write base class info here\n";
}
};
inline std::ostream& operator<<(std::ostream& os, const base& b) {
b.write_to(os);
return os;
}
struct derived : base {
private:
virtual void handle_write(std::ostream& os) const override {
base::handle_write(os); // handle base class implementation
std::cout << "write relevant data here. We could still be overridden safely\n";
}
};
// write any derived class like this:
auto b = unique_ptr<base> { new derived() };
cout << "here is a kind-of b:\n" << *b << endl;